diff --git a/vendor/github.com/caddyserver/certmagic/acmeclient.go b/vendor/github.com/caddyserver/certmagic/acmeclient.go deleted file mode 100644 index ea06e96..0000000 --- a/vendor/github.com/caddyserver/certmagic/acmeclient.go +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package certmagic - -import ( - "context" - "crypto/tls" - "encoding/base64" - "fmt" - "log" - weakrand "math/rand" - "net" - "net/http" - "net/url" - "strconv" - "strings" - "sync" - "time" - - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/certificate" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/lego" - "github.com/go-acme/lego/v3/registration" -) - -func init() { - weakrand.Seed(time.Now().UnixNano()) -} - -// acmeClient is a wrapper over lego's acme.Client with -// some custom state attached. It is used to obtain, -// renew, and revoke certificates with ACME. Use -// ACMEManager.newACMEClient() or -// ACMEManager.newACMEClientWithRetry() to get a valid -// one for real use. -type acmeClient struct { - caURL string - mgr *ACMEManager - acmeClient *lego.Client - challenges []challenge.Type -} - -// newACMEClientWithRetry is the same as newACMEClient, but with -// automatic retry capabilities. Sometimes network connections or -// HTTP requests fail intermittently, even when requesting the -// directory endpoint for example, so we can avoid that by just -// retrying once. Failures here are rare and sporadic, usually, -// so a simple retry is an easy fix. -func (am *ACMEManager) newACMEClientWithRetry(useTestCA bool) (*acmeClient, error) { - var client *acmeClient - var err error - const maxTries = 2 - for i := 0; i < maxTries; i++ { - if i > 0 { - time.Sleep(2 * time.Second) - } - client, err = am.newACMEClient(useTestCA, false) // TODO: move logic that requires interactivity to way before this part of the process... - if err == nil { - break - } - if acmeErr, ok := err.(acme.ProblemDetails); ok { - if acmeErr.HTTPStatus == http.StatusTooManyRequests { - return nil, fmt.Errorf("too many requests making new ACME client: %+v - aborting", acmeErr) - } - } - log.Printf("[ERROR] Making new ACME client: %v (attempt %d/%d)", err, i+1, maxTries) - } - return client, err -} - -// newACMEClient creates the underlying ACME library client type. -// If useTestCA is true, am.TestCA will be used if it is set; -// otherwise, the primary CA will still be used. -func (am *ACMEManager) newACMEClient(useTestCA, interactive bool) (*acmeClient, error) { - acmeClientsMu.Lock() - defer acmeClientsMu.Unlock() - - // ensure defaults are filled in - certObtainTimeout := am.CertObtainTimeout - if certObtainTimeout == 0 { - certObtainTimeout = DefaultACME.CertObtainTimeout - } - var caURL string - if useTestCA { - caURL = am.TestCA - // only use the default test CA if the CA is also - // the default CA; no point in testing against - // Let's Encrypt's staging server if we are not - // using their production server too - if caURL == "" && am.CA == DefaultACME.CA { - caURL = DefaultACME.TestCA - } - } - if caURL == "" { - caURL = am.CA - } - if caURL == "" { - caURL = DefaultACME.CA - } - - // ensure endpoint is secure (assume HTTPS if scheme is missing) - if !strings.Contains(caURL, "://") { - caURL = "https://" + caURL - } - u, err := url.Parse(caURL) - if err != nil { - return nil, err - } - if u.Scheme != "https" && !isLoopback(u.Host) && !isInternal(u.Host) { - return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) - } - - // look up or create the user account - leUser, err := am.getUser(caURL, am.Email) - if err != nil { - return nil, err - } - - // if a lego client with this configuration already exists, reuse it - clientKey := caURL + leUser.Email - client, ok := acmeClients[clientKey] - if !ok { - // the client facilitates our communication with the CA server - legoCfg := lego.NewConfig(leUser) - legoCfg.CADirURL = caURL - legoCfg.UserAgent = buildUAString() - legoCfg.HTTPClient.Timeout = HTTPTimeout - legoCfg.Certificate = lego.CertificateConfig{ - Timeout: am.CertObtainTimeout, - } - if am.TrustedRoots != nil { - if ht, ok := legoCfg.HTTPClient.Transport.(*http.Transport); ok { - if ht.TLSClientConfig == nil { - ht.TLSClientConfig = new(tls.Config) - ht.ForceAttemptHTTP2 = true - } - ht.TLSClientConfig.RootCAs = am.TrustedRoots - } - } - client, err = lego.NewClient(legoCfg) - if err != nil { - return nil, err - } - acmeClients[clientKey] = client - } - - // if not registered, the user must register an account - // with the CA and agree to terms - if leUser.Registration == nil { - if interactive { // can't prompt a user who isn't there - termsURL := client.GetToSURL() - if !am.Agreed && termsURL != "" { - am.Agreed = am.askUserAgreement(client.GetToSURL()) - } - if !am.Agreed && termsURL != "" { - return nil, fmt.Errorf("user must agree to CA terms") - } - } - - var reg *registration.Resource - if am.ExternalAccount != nil { - reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: am.Agreed, - Kid: am.ExternalAccount.KeyID, - HmacEncoded: base64.StdEncoding.EncodeToString(am.ExternalAccount.HMAC), - }) - } else { - reg, err = client.Registration.Register(registration.RegisterOptions{ - TermsOfServiceAgreed: am.Agreed, - }) - } - if err != nil { - return nil, err - } - leUser.Registration = reg - - // persist the user to storage - err = am.saveUser(caURL, leUser) - if err != nil { - return nil, fmt.Errorf("could not save user: %v", err) - } - } - - c := &acmeClient{ - caURL: caURL, - mgr: am, - acmeClient: client, - } - - return c, nil -} - -// initialChallenges returns the initial set of challenges -// to try using c.config as a basis. -func (c *acmeClient) initialChallenges() []challenge.Type { - // if configured, use DNS challenge exclusively - if c.mgr.DNSProvider != nil { - return []challenge.Type{challenge.DNS01} - } - - // otherwise, use HTTP and TLS-ALPN challenges if enabled - var chal []challenge.Type - if !c.mgr.DisableHTTPChallenge { - chal = append(chal, challenge.HTTP01) - } - if !c.mgr.DisableTLSALPNChallenge { - chal = append(chal, challenge.TLSALPN01) - } - return chal -} - -// nextChallenge chooses a challenge randomly from the given list of -// available challenges and configures c.acmeClient to use that challenge -// according to c.config. It pops the chosen challenge from the list and -// returns that challenge along with the new list without that challenge. -// If len(available) == 0, this is a no-op. -// -// Don't even get me started on how dumb it is we need to do this here -// instead of the upstream lego library doing it for us. Lego used to -// randomize the challenge order, thus allowing another one to be used -// if the first one failed. https://github.com/go-acme/lego/issues/842 -// (It also has an awkward API for adjusting the available challenges.) -// At time of writing, lego doesn't try anything other than the TLS-ALPN -// challenge, even if the HTTP challenge is also enabled. So we take -// matters into our own hands and enable only one challenge at a time -// in the underlying client, randomly selected by us. -func (c *acmeClient) nextChallenge(available []challenge.Type) (challenge.Type, []challenge.Type) { - if len(available) == 0 { - return "", available - } - - // make sure we choose a challenge randomly, which lego used to do but - // the critical feature was surreptitiously removed in ~2018 in a commit - // too large to review, oh well - choose one, then remove it from the - // list of available challenges so it doesn't get retried - randIdx := weakrand.Intn(len(available)) - randomChallenge := available[randIdx] - available = append(available[:randIdx], available[randIdx+1:]...) - - // clean the slate, since we reuse clients - c.acmeClient.Challenge.Remove(challenge.HTTP01) - c.acmeClient.Challenge.Remove(challenge.TLSALPN01) - c.acmeClient.Challenge.Remove(challenge.DNS01) - - switch randomChallenge { - case challenge.HTTP01: - useHTTPPort := HTTPChallengePort - if HTTPPort > 0 && HTTPPort != HTTPChallengePort { - useHTTPPort = HTTPPort - } - if c.mgr.AltHTTPPort > 0 { - useHTTPPort = c.mgr.AltHTTPPort - } - - c.acmeClient.Challenge.SetHTTP01Provider(distributedSolver{ - acmeManager: c.mgr, - providerServer: &httpSolver{ - acmeManager: c.mgr, - address: net.JoinHostPort(c.mgr.ListenHost, strconv.Itoa(useHTTPPort)), - }, - caURL: c.caURL, - }) - - case challenge.TLSALPN01: - useTLSALPNPort := TLSALPNChallengePort - if HTTPSPort > 0 && HTTPSPort != TLSALPNChallengePort { - useTLSALPNPort = HTTPSPort - } - if c.mgr.AltTLSALPNPort > 0 { - useTLSALPNPort = c.mgr.AltTLSALPNPort - } - - c.acmeClient.Challenge.SetTLSALPN01Provider(distributedSolver{ - acmeManager: c.mgr, - providerServer: &tlsALPNSolver{ - config: c.mgr.config, - address: net.JoinHostPort(c.mgr.ListenHost, strconv.Itoa(useTLSALPNPort)), - }, - caURL: c.caURL, - }) - - case challenge.DNS01: - if c.mgr.DNSChallengeOption != nil { - c.acmeClient.Challenge.SetDNS01Provider(c.mgr.DNSProvider, c.mgr.DNSChallengeOption) - } else { - c.acmeClient.Challenge.SetDNS01Provider(c.mgr.DNSProvider) - } - } - - return randomChallenge, available -} - -func (c *acmeClient) throttle(ctx context.Context, names []string) error { - // throttling is scoped to CA + account email - rateLimiterKey := c.caURL + "," + c.mgr.Email - rateLimitersMu.Lock() - rl, ok := rateLimiters[rateLimiterKey] - if !ok { - rl = NewRateLimiter(RateLimitEvents, RateLimitEventsWindow) - rateLimiters[rateLimiterKey] = rl - // TODO: stop rate limiter when it is garbage-collected... - } - rateLimitersMu.Unlock() - log.Printf("[INFO]%v Waiting on rate limiter...", names) - err := rl.Wait(ctx) - if err != nil { - return err - } - log.Printf("[INFO]%v Done waiting", names) - return nil -} - -func (c *acmeClient) usingTestCA() bool { - return c.mgr.TestCA != "" && c.caURL == c.mgr.TestCA -} - -func (c *acmeClient) revoke(_ context.Context, certRes certificate.Resource) error { - return c.acmeClient.Certificate.Revoke(certRes.Certificate) -} - -func buildUAString() string { - ua := "CertMagic" - if UserAgent != "" { - ua += " " + UserAgent - } - return ua -} - -// These internal rate limits are designed to prevent accidentally -// firehosing a CA's ACME endpoints. They are not intended to -// replace or replicate the CA's actual rate limits. -// -// Let's Encrypt's rate limits can be found here: -// https://letsencrypt.org/docs/rate-limits/ -// -// Currently (as of December 2019), Let's Encrypt's most relevant -// rate limit for large deployments is 300 new orders per account -// per 3 hours (on average, or best case, that's about 1 every 36 -// seconds, or 2 every 72 seconds, etc.); but it's not reasonable -// to try to assume that our internal state is the same as the CA's -// (due to process restarts, config changes, failed validations, -// etc.) and ultimately, only the CA's actual rate limiter is the -// authority. Thus, our own rate limiters do not attempt to enforce -// external rate limits. Doing so causes problems when the domains -// are not in our control (i.e. serving customer sites) and/or lots -// of domains fail validation: they clog our internal rate limiter -// and nearly starve out (or at least slow down) the other domains -// that need certificates. Failed transactions are already retried -// with exponential backoff, so adding in rate limiting can slow -// things down even more. -// -// Instead, the point of our internal rate limiter is to avoid -// hammering the CA's endpoint when there are thousands or even -// millions of certificates under management. Our goal is to -// allow small bursts in a relatively short timeframe so as to -// not block any one domain for too long, without unleashing -// thousands of requests to the CA at once. -var ( - rateLimiters = make(map[string]*RingBufferRateLimiter) - rateLimitersMu sync.RWMutex - - // RateLimitEvents is how many new events can be allowed - // in RateLimitEventsWindow. - RateLimitEvents = 10 - - // RateLimitEventsWindow is the size of the sliding - // window that throttles events. - RateLimitEventsWindow = 1 * time.Minute -) - -// Some default values passed down to the underlying lego client. -var ( - UserAgent string - HTTPTimeout = 30 * time.Second -) - -// We keep a global cache of ACME clients so that they -// can be reused. Since the number of CAs, accounts, -// and key types should be fairly limited under best -// practices, this map will hardly ever have more than -// a few entries at the most. The associated lock -// protects access to the map but also ensures that only -// one ACME client is created at a time. -// TODO: consider using storage for a distributed lock -// TODO: consider evicting clients after some time -var ( - acmeClients = make(map[string]*lego.Client) - acmeClientsMu sync.Mutex -) diff --git a/vendor/github.com/caddyserver/certmagic/azure-pipelines.yml b/vendor/github.com/caddyserver/certmagic/azure-pipelines.yml deleted file mode 100644 index fbdaf52..0000000 --- a/vendor/github.com/caddyserver/certmagic/azure-pipelines.yml +++ /dev/null @@ -1,85 +0,0 @@ -trigger: -- master - -strategy: - matrix: - linux: - imageName: ubuntu-16.04 - gorootDir: /usr/local - mac: - imageName: macos-10.13 - gorootDir: /usr/local - windows: - imageName: windows-2019 - gorootDir: C:\ - -pool: - vmImage: $(imageName) - -variables: - GOROOT: $(gorootDir)/go - GOPATH: $(system.defaultWorkingDirectory)/gopath - GOBIN: $(GOPATH)/bin - modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' - # TODO: modules should be the default in Go 1.13, so this won't be needed - #GO111MODULE: on - -steps: -- bash: | - latestGo=$(curl "https://golang.org/VERSION?m=text") - echo "##vso[task.setvariable variable=LATEST_GO]$latestGo" - echo "Latest Go version: $latestGo" - displayName: "Get latest Go version" - -- bash: | - sudo rm -f $(which go) - echo '##vso[task.prependpath]$(GOBIN)' - echo '##vso[task.prependpath]$(GOROOT)/bin' - mkdir -p '$(modulePath)' - shopt -s extglob - shopt -s dotglob - mv !(gopath) '$(modulePath)' - displayName: Remove old Go, set GOBIN/GOROOT, and move project into GOPATH - -# Install Go (this varies by platform) - -- bash: | - wget "https://dl.google.com/go/$(LATEST_GO).linux-amd64.tar.gz" - sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).linux-amd64.tar.gz" - condition: eq( variables['Agent.OS'], 'Linux' ) - displayName: Install Go on Linux - -- bash: | - wget "https://dl.google.com/go/$(LATEST_GO).darwin-amd64.tar.gz" - sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).darwin-amd64.tar.gz" - condition: eq( variables['Agent.OS'], 'Darwin' ) - displayName: Install Go on macOS - -- powershell: | - Write-Host "Downloading Go... (please be patient, I am very slow)" - (New-Object System.Net.WebClient).DownloadFile("https://dl.google.com/go/$(LATEST_GO).windows-amd64.zip", "$(LATEST_GO).windows-amd64.zip") - Write-Host "Extracting Go... (I'm slow too)" - Expand-Archive "$(LATEST_GO).windows-amd64.zip" -DestinationPath "$(gorootDir)" - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - displayName: Install Go on Windows - -# TODO: When this issue is fixed, replace with installer script: -# https://github.com/golangci/golangci-lint/issues/472 -- script: go get -v github.com/golangci/golangci-lint/cmd/golangci-lint - displayName: Install golangci-lint - -- bash: | - printf "Using go at: $(which go)\n" - printf "Go version: $(go version)\n" - printf "\n\nGo environment:\n\n" - go env - printf "\n\nSystem environment:\n\n" - env - displayName: Print Go version and environment - -- script: | - go get -v -t -d ./... - golangci-lint run -E gofmt -E goimports -E misspell - go test -race ./... - workingDirectory: '$(modulePath)' - displayName: Run tests diff --git a/vendor/github.com/caddyserver/certmagic/go.mod b/vendor/github.com/caddyserver/certmagic/go.mod deleted file mode 100644 index e3c21f2..0000000 --- a/vendor/github.com/caddyserver/certmagic/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/caddyserver/certmagic - -go 1.13 - -require ( - github.com/go-acme/lego/v3 v3.4.0 - github.com/klauspost/cpuid v1.2.3 - golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 -) diff --git a/vendor/github.com/caddyserver/certmagic/go.sum b/vendor/github.com/caddyserver/certmagic/go.sum deleted file mode 100644 index 17c0626..0000000 --- a/vendor/github.com/caddyserver/certmagic/go.sum +++ /dev/null @@ -1,380 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= -github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= -github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= -github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= -github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= -github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= -github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= -github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU= -github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= -github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= -github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A= -github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M= -github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= -github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= -github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= -github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI= -github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= -github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= -github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= -github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= -github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= -github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= -github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= -golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA= -golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a h1:Yu34BogBivvmu7SAzHHaB9nZWH5D1C+z3F1jyIaYZSQ= -golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= -gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/vendor/github.com/caddyserver/certmagic/.gitignore b/vendor/github.com/coolaj86/certmagic/.gitignore similarity index 100% rename from vendor/github.com/caddyserver/certmagic/.gitignore rename to vendor/github.com/coolaj86/certmagic/.gitignore diff --git a/vendor/github.com/caddyserver/certmagic/LICENSE.txt b/vendor/github.com/coolaj86/certmagic/LICENSE.txt similarity index 100% rename from vendor/github.com/caddyserver/certmagic/LICENSE.txt rename to vendor/github.com/coolaj86/certmagic/LICENSE.txt diff --git a/vendor/github.com/caddyserver/certmagic/README.md b/vendor/github.com/coolaj86/certmagic/README.md similarity index 93% rename from vendor/github.com/caddyserver/certmagic/README.md rename to vendor/github.com/coolaj86/certmagic/README.md index f8f09bc..0bd6c55 100644 --- a/vendor/github.com/caddyserver/certmagic/README.md +++ b/vendor/github.com/coolaj86/certmagic/README.md @@ -5,7 +5,7 @@

The same library used by the Caddy Web Server

- +

@@ -90,8 +90,9 @@ CertMagic - Automatic HTTPS using Let's Encrypt - Robust retries for up to 30 days - Exponential backoff with carefully-tuned intervals - Retries with optional test/staging CA endpoint instead of production, to avoid rate limits -- Over 50 DNS providers work out-of-the-box (powered by [lego](https://github.com/go-acme/lego)!) - Written in Go, a language with memory-safety guarantees +- Powered by [ACMEz](https://github.com/mholt/acmez), _the_ premier ACME client library for Go +- All [libdns](https://github.com/libdns) DNS providers work out-of-the-box - Pluggable storage implementations (default: file system) - Wildcard certificates - Automatic OCSP stapling ([done right](https://gist.github.com/sleevi/5efe9ef98961ecfb4da8#gistcomment-2336055)) [keeps your sites online!](https://twitter.com/caddyserver/status/1234874273724084226) @@ -248,9 +249,9 @@ cache := certmagic.NewCache(certmagic.CacheOptions{ // mind that this config value is used as a // template, and will be completed with any // defaults that are set in the Default config - return certmagic.Config{ + return &certmagic.Config{ // ... - }), nil + }, nil }, ... }) @@ -385,30 +386,21 @@ ln, err := tls.Listen("tcp", ":443", myTLSConfig) The DNS challenge is perhaps the most useful challenge because it allows you to obtain certificates without your server needing to be publicly accessible on the Internet, and it's the only challenge by which Let's Encrypt will issue wildcard certificates. -This challenge works by setting a special record in the domain's zone. To do this automatically, your DNS provider needs to offer an API by which changes can be made to domain names, and the changes need to take effect immediately for best results. CertMagic supports [all of lego's DNS provider implementations](https://github.com/go-acme/lego/tree/master/providers/dns)! All of them clean up the temporary record after the challenge completes. +This challenge works by setting a special record in the domain's zone. To do this automatically, your DNS provider needs to offer an API by which changes can be made to domain names, and the changes need to take effect immediately for best results. CertMagic supports [all DNS providers with `libdns` implementations](https://github.com/libdns)! It always cleans up the temporary record after the challenge completes. -To enable it, just set the `DNSProvider` field on a `certmagic.Config` struct, or set the default `certmagic.DNSProvider` variable. For example, if my domains' DNS was served by DNSimple and I set my DNSimple API credentials in environment variables: +To enable it, just set the `DNS01Solver` field on a `certmagic.ACMEManager` struct, or set the default `certmagic.ACMEManager.DNS01Solver` variable. For example, if my domains' DNS was served by Cloudflare: ```go -import "github.com/go-acme/lego/v3/providers/dns/dnsimple" +import "github.com/libdns/cloudflare" -provider, err := dnsimple.NewDNSProvider() -if err != nil { - return err +certmagic.DefaultACME.DNS01Solver = &certmagic.DNS01Solver{ + DNSProvider: cloudflare.Provider{ + APIToken: "topsecret", + }, } - -certmagic.DefaultACME.DNSProvider = provider ``` -Now the DNS challenge will be used by default, and I can obtain certificates for wildcard domains. See the [pkg.go.dev documentation for the provider you're using](https://pkg.go.dev/github.com/go-acme/lego/providers/dns?tab=subdirectories) to learn how to configure it. Most can be configured by env variables or by passing in a config struct. If you pass a config struct instead of using env variables, you will probably need to set some other defaults (that's just how lego works, currently): - -```go -PropagationTimeout: dns01.DefaultPollingInterval, -PollingInterval: dns01.DefaultPollingInterval, -TTL: dns01.DefaultTTL, -``` - -Enabling the DNS challenge disables the other challenges for that `certmagic.Config` instance. +Now the DNS challenge will be used by default, and I can obtain certificates for wildcard domains, too. Enabling the DNS challenge disables the other challenges for that `certmagic.ACMEManager` instance. ## On-Demand TLS @@ -503,7 +495,7 @@ We welcome your contributions! Please see our **[contributing guidelines](https: ## Project History -CertMagic is the core of Caddy's advanced TLS automation code, extracted into a library. The underlying ACME client implementation is [lego](https://github.com/go-acme/lego), which was originally developed for use in Caddy even before Let's Encrypt entered public beta in 2015. +CertMagic is the core of Caddy's advanced TLS automation code, extracted into a library. The underlying ACME client implementation is [ACMEz](https://github.com/mholt/acmez). CertMagic's code was originally a central part of Caddy even before Let's Encrypt entered public beta in 2015. In the years since then, Caddy's TLS automation techniques have been widely adopted, tried and tested in production, and served millions of sites and secured trillions of connections. diff --git a/vendor/github.com/caddyserver/certmagic/user.go b/vendor/github.com/coolaj86/certmagic/account.go similarity index 58% rename from vendor/github.com/caddyserver/certmagic/user.go rename to vendor/github.com/coolaj86/certmagic/account.go index c105712..e1ebb32 100644 --- a/vendor/github.com/caddyserver/certmagic/user.go +++ b/vendor/github.com/coolaj86/certmagic/account.go @@ -16,58 +16,138 @@ package certmagic import ( "bufio" - "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "encoding/json" "fmt" "io" - "net/http" "os" "path" "sort" "strings" - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/registration" + "github.com/mholt/acmez/acme" ) -// user represents a Let's Encrypt user account. -type user struct { - Email string - Registration *registration.Resource - key crypto.PrivateKey -} - -// GetEmail gets u's email. -func (u user) GetEmail() string { - return u.Email -} - -// GetRegistration gets u's registration resource. -func (u user) GetRegistration() *registration.Resource { - return u.Registration -} - -// GetPrivateKey gets u's private key. -func (u user) GetPrivateKey() crypto.PrivateKey { - return u.key -} - -// newUser creates a new User for the given email address -// with a new private key. This function does NOT save the -// user to disk or register it via ACME. If you want to use -// a user account that might already exist, call getUser -// instead. It does NOT prompt the user. -func (*ACMEManager) newUser(email string) (*user, error) { - user := &user{Email: email} - privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) +// getAccount either loads or creates a new account, depending on if +// an account can be found in storage for the given CA + email combo. +func (am *ACMEManager) getAccount(ca, email string) (acme.Account, error) { + regBytes, err := am.config.Storage.Load(am.storageKeyUserReg(ca, email)) if err != nil { - return user, fmt.Errorf("generating private key: %v", err) + if _, ok := err.(ErrNotExist); ok { + return am.newAccount(email) + } + return acme.Account{}, err } - user.key = privateKey - return user, nil + keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(ca, email)) + if err != nil { + if _, ok := err.(ErrNotExist); ok { + return am.newAccount(email) + } + return acme.Account{}, err + } + + var acct acme.Account + err = json.Unmarshal(regBytes, &acct) + if err != nil { + return acct, err + } + acct.PrivateKey, err = decodePrivateKey(keyBytes) + if err != nil { + return acct, fmt.Errorf("could not decode account's private key: %v", err) + } + + // TODO: July 2020 - transition to new ACME lib and account structure; + // for a while, we will need to convert old accounts to new structure + acct, err = am.transitionAccountToACMEzJuly2020Format(ca, acct, regBytes) + if err != nil { + return acct, fmt.Errorf("one-time account transition: %v", err) + } + + return acct, err +} + +// TODO: this is a temporary transition helper starting July 2020. +// It can go away when we think enough time has passed that most active assets have transitioned. +func (am *ACMEManager) transitionAccountToACMEzJuly2020Format(ca string, acct acme.Account, regBytes []byte) (acme.Account, error) { + if acct.Status != "" && acct.Location != "" { + return acct, nil + } + + var oldAcct struct { + Email string `json:"Email"` + Registration struct { + Body struct { + Status string `json:"status"` + TermsOfServiceAgreed bool `json:"termsOfServiceAgreed"` + Orders string `json:"orders"` + ExternalAccountBinding json.RawMessage `json:"externalAccountBinding"` + } `json:"body"` + URI string `json:"uri"` + } `json:"Registration"` + } + err := json.Unmarshal(regBytes, &oldAcct) + if err != nil { + return acct, fmt.Errorf("decoding into old account type: %v", err) + } + + acct.Status = oldAcct.Registration.Body.Status + acct.TermsOfServiceAgreed = oldAcct.Registration.Body.TermsOfServiceAgreed + acct.Location = oldAcct.Registration.URI + acct.ExternalAccountBinding = oldAcct.Registration.Body.ExternalAccountBinding + acct.Orders = oldAcct.Registration.Body.Orders + if oldAcct.Email != "" { + acct.Contact = []string{"mailto:" + oldAcct.Email} + } + + err = am.saveAccount(ca, acct) + if err != nil { + return acct, fmt.Errorf("saving converted account: %v", err) + } + + return acct, nil +} + +// newAccount generates a new private key for a new ACME account, but +// it does not register or save the account. +func (*ACMEManager) newAccount(email string) (acme.Account, error) { + var acct acme.Account + if email != "" { + acct.Contact = []string{"mailto:" + email} // TODO: should we abstract the contact scheme? + } + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return acct, fmt.Errorf("generating private key: %v", err) + } + acct.PrivateKey = privateKey + return acct, nil +} + +// saveAccount persists an ACME account's info and private key to storage. +// It does NOT register the account via ACME or prompt the user. +func (am *ACMEManager) saveAccount(ca string, account acme.Account) error { + regBytes, err := json.MarshalIndent(account, "", "\t") + if err != nil { + return err + } + keyBytes, err := encodePrivateKey(account.PrivateKey) + if err != nil { + return err + } + // extract primary contact (email), without scheme (e.g. "mailto:") + primaryContact := getPrimaryContact(account) + all := []keyValue{ + { + key: am.storageKeyUserReg(ca, primaryContact), + value: regBytes, + }, + { + key: am.storageKeyUserPrivateKey(ca, primaryContact), + value: keyBytes, + }, + } + return storeTx(am.config.Storage, all) } // getEmail does everything it can to obtain an email address @@ -82,13 +162,13 @@ func (am *ACMEManager) getEmail(allowPrompts bool) error { // First try package default email if leEmail == "" { - leEmail = DefaultACME.Email // TODO: racey with line 108 + leEmail = DefaultACME.Email // TODO: racey with line 122 (or whichever line assigns to DefaultACME.Email below) } // Then try to get most recent user email from storage var gotRecentEmail bool if leEmail == "" { - leEmail, gotRecentEmail = am.mostRecentUserEmail(am.CA) + leEmail, gotRecentEmail = am.mostRecentAccountEmail(am.CA) } if !gotRecentEmail && leEmail == "" && allowPrompts { // Looks like there is no email address readily available, @@ -105,46 +185,21 @@ func (am *ACMEManager) getEmail(allowPrompts bool) error { // save the email for later and ensure it is consistent // for repeated use; then update cfg with the email - DefaultACME.Email = strings.TrimSpace(strings.ToLower(leEmail)) // TODO: this is racey with line 85 + DefaultACME.Email = strings.TrimSpace(strings.ToLower(leEmail)) // TODO: this is racey with line 99 am.Email = DefaultACME.Email return nil } -func (am *ACMEManager) getAgreementURL() (string, error) { - if agreementTestURL != "" { - return agreementTestURL, nil - } - caURL := am.CA - if caURL == "" { - caURL = DefaultACME.CA - } - response, err := http.Get(caURL) - if err != nil { - return "", err - } - defer response.Body.Close() - var dir acme.Directory - err = json.NewDecoder(response.Body).Decode(&dir) - if err != nil { - return "", err - } - return dir.Meta.TermsOfService, nil -} - // promptUserForEmail prompts the user for an email address // and returns the email address they entered (which could // be the empty string). If no error is returned, then Agreed // will also be set to true, since continuing through the // prompt signifies agreement. func (am *ACMEManager) promptUserForEmail() (string, error) { - agreementURL, err := am.getAgreementURL() - if err != nil { - return "", fmt.Errorf("get Agreement URL: %v", err) - } // prompt the user for an email address and terms agreement reader := bufio.NewReader(stdin) - am.promptUserAgreement(agreementURL) + am.promptUserAgreement("") fmt.Println("Please enter your email address to signify agreement and to be notified") fmt.Println("in case of issues. You can leave it blank, but we don't recommend it.") fmt.Print(" Email address: ") @@ -157,72 +212,17 @@ func (am *ACMEManager) promptUserForEmail() (string, error) { return leEmail, nil } -// getUser loads the user with the given email from disk -// using the provided storage. If the user does not exist, -// it will create a new one, but it does NOT save new -// users to the disk or register them via ACME. It does -// NOT prompt the user. -func (am *ACMEManager) getUser(ca, email string) (*user, error) { - regBytes, err := am.config.Storage.Load(am.storageKeyUserReg(ca, email)) - if err != nil { - if _, ok := err.(ErrNotExist); ok { - // create a new user - return am.newUser(email) - } - return nil, err - } - keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(ca, email)) - if err != nil { - if _, ok := err.(ErrNotExist); ok { - // create a new user - return am.newUser(email) - } - return nil, err - } - - var u *user - err = json.Unmarshal(regBytes, &u) - if err != nil { - return u, err - } - u.key, err = decodePrivateKey(keyBytes) - return u, err -} - -// saveUser persists a user's key and account registration -// to the file system. It does NOT register the user via ACME -// or prompt the user. You must also pass in the storage -// wherein the user should be saved. It should be the storage -// for the CA with which user has an account. -func (am *ACMEManager) saveUser(ca string, user *user) error { - regBytes, err := json.MarshalIndent(&user, "", "\t") - if err != nil { - return err - } - keyBytes, err := encodePrivateKey(user.key) - if err != nil { - return err - } - all := []keyValue{ - { - key: am.storageKeyUserReg(ca, user.Email), - value: regBytes, - }, - { - key: am.storageKeyUserPrivateKey(ca, user.Email), - value: keyBytes, - }, - } - return storeTx(am.config.Storage, all) -} - // promptUserAgreement simply outputs the standard user // agreement prompt with the given agreement URL. // It outputs a newline after the message. func (am *ACMEManager) promptUserAgreement(agreementURL string) { - const userAgreementPrompt = `Your sites will be served over HTTPS automatically using Let's Encrypt. -By continuing, you agree to the Let's Encrypt Subscriber Agreement at:` - fmt.Printf("\n\n%s\n %s\n", userAgreementPrompt, agreementURL) + userAgreementPrompt := `Your sites will be served over HTTPS automatically using an automated CA. +By continuing, you agree to the CA's terms of service` + if agreementURL == "" { + fmt.Printf("\n\n%s.\n", userAgreementPrompt) + return + } + fmt.Printf("\n\n%s at:\n %s\n", userAgreementPrompt, agreementURL) } // askUserAgreement prompts the user to agree to the agreement @@ -292,20 +292,20 @@ func (*ACMEManager) emailUsername(email string) string { return email[:at] } -// mostRecentUserEmail finds the most recently-written user file +// mostRecentAccountEmail finds the most recently-written account file // in storage. Since this is part of a complex sequence to get a user // account, errors here are discarded to simplify code flow in // the caller, and errors are not important here anyway. -func (am *ACMEManager) mostRecentUserEmail(caURL string) (string, bool) { - userList, err := am.config.Storage.List(am.storageKeyUsersPrefix(caURL), false) - if err != nil || len(userList) == 0 { +func (am *ACMEManager) mostRecentAccountEmail(caURL string) (string, bool) { + accountList, err := am.config.Storage.List(am.storageKeyUsersPrefix(caURL), false) + if err != nil || len(accountList) == 0 { return "", false } // get all the key infos ahead of sorting, because // we might filter some out stats := make(map[string]KeyInfo) - for i, u := range userList { + for i, u := range accountList { keyInfo, err := am.config.Storage.Stat(u) if err != nil { continue @@ -317,24 +317,42 @@ func (am *ACMEManager) mostRecentUserEmail(caURL string) (string, bool) { // which existed... sure, this isn't a perfect fix but // frankly one's OS shouldn't mess with the data folder // in the first place. - userList = append(userList[:i], userList[i+1:]...) + accountList = append(accountList[:i], accountList[i+1:]...) continue } stats[u] = keyInfo } - sort.Slice(userList, func(i, j int) bool { - iInfo := stats[userList[i]] - jInfo := stats[userList[j]] + sort.Slice(accountList, func(i, j int) bool { + iInfo := stats[accountList[i]] + jInfo := stats[accountList[j]] return jInfo.Modified.Before(iInfo.Modified) }) - user, err := am.getUser(caURL, path.Base(userList[0])) + if len(accountList) == 0 { + return "", false + } + + account, err := am.getAccount(caURL, path.Base(accountList[0])) if err != nil { return "", false } - return user.Email, true + return getPrimaryContact(account), true +} + +// getPrimaryContact returns the first contact on the account (if any) +// without the scheme. (I guess we assume an email address.) +func getPrimaryContact(account acme.Account) string { + // TODO: should this be abstracted with some lower-level helper? + var primaryContact string + if len(account.Contact) > 0 { + primaryContact = account.Contact[0] + if idx := strings.Index(primaryContact, ":"); idx >= 0 { + primaryContact = primaryContact[idx+1:] + } + } + return primaryContact } // agreementTestURL is set during tests to skip requiring diff --git a/vendor/github.com/coolaj86/certmagic/acmeclient.go b/vendor/github.com/coolaj86/certmagic/acmeclient.go new file mode 100644 index 0000000..772fe57 --- /dev/null +++ b/vendor/github.com/coolaj86/certmagic/acmeclient.go @@ -0,0 +1,340 @@ +// Copyright 2015 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package certmagic + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + weakrand "math/rand" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + "time" + + "github.com/mholt/acmez" + "github.com/mholt/acmez/acme" + "go.uber.org/zap" +) + +func init() { + weakrand.Seed(time.Now().UnixNano()) +} + +// acmeClient holds state necessary for us to perform +// ACME operations for certificate management. Call +// ACMEManager.newACMEClient() to get a valid one to . +type acmeClient struct { + mgr *ACMEManager + acmeClient *acmez.Client + account acme.Account +} + +// newACMEClient creates the underlying ACME library client type. +// If useTestCA is true, am.TestCA will be used if it is set; +// otherwise, the primary CA will still be used. +func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive bool) (*acmeClient, error) { + // ensure defaults are filled in + var caURL string + if useTestCA { + caURL = am.TestCA + } + if caURL == "" { + caURL = am.CA + } + if caURL == "" { + caURL = DefaultACME.CA + } + certObtainTimeout := am.CertObtainTimeout + if certObtainTimeout == 0 { + certObtainTimeout = DefaultACME.CertObtainTimeout + } + + // ensure endpoint is secure (assume HTTPS if scheme is missing) + if !strings.Contains(caURL, "://") { + caURL = "https://" + caURL + } + u, err := url.Parse(caURL) + if err != nil { + return nil, err + } + if u.Scheme != "https" && !isLoopback(u.Host) && !isInternal(u.Host) { + return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) + } + + // look up or create the ACME account + account, err := am.getAccount(caURL, am.Email) + if err != nil { + return nil, fmt.Errorf("getting ACME account: %v", err) + } + + // set up the dialers and resolver for the ACME client's HTTP client + dialer := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 2 * time.Minute, + } + if am.Resolver != "" { + dialer.Resolver = &net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, _ string) (net.Conn, error) { + return (&net.Dialer{ + Timeout: 15 * time.Second, + }).DialContext(ctx, network, am.Resolver) + }, + } + } + + // TODO: we could potentially reuse the HTTP transport and client + hc := am.httpClient // TODO: is this racey? + if am.httpClient == nil { + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: dialer.DialContext, + TLSHandshakeTimeout: 15 * time.Second, + ResponseHeaderTimeout: 15 * time.Second, + ExpectContinueTimeout: 2 * time.Second, + ForceAttemptHTTP2: true, + } + if am.TrustedRoots != nil { + transport.TLSClientConfig = &tls.Config{ + RootCAs: am.TrustedRoots, + } + } + + hc = &http.Client{ + Transport: transport, + Timeout: HTTPTimeout, + } + + am.httpClient = hc + } + + client := &acmez.Client{ + Client: &acme.Client{ + Directory: caURL, + PollTimeout: certObtainTimeout, + UserAgent: buildUAString(), + HTTPClient: hc, + }, + ChallengeSolvers: make(map[string]acmez.Solver), + } + if am.Logger != nil { + l := am.Logger.Named("acme_client") + client.Client.Logger, client.Logger = l, l + } + + // configure challenges (most of the time, DNS challenge is + // exclusive of other ones because it is usually only used + // in situations where the default challenges would fail) + if am.DNS01Solver == nil { + // enable HTTP-01 challenge + if !am.DisableHTTPChallenge && am.HTTP01Solver == nil { + useHTTPPort := HTTPChallengePort + if HTTPPort > 0 && HTTPPort != HTTPChallengePort { + useHTTPPort = HTTPPort + } + if am.AltHTTPPort > 0 { + useHTTPPort = am.AltHTTPPort + } + am.HTTP01Solver = distributedSolver{ + acmeManager: am, + solver: &httpSolver{ + acmeManager: am, + address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useHTTPPort)), + }, + caURL: client.Directory, + } + } + client.ChallengeSolvers[acme.ChallengeTypeHTTP01] = am.HTTP01Solver + + // enable TLS-ALPN-01 challenge + if !am.DisableTLSALPNChallenge { + useTLSALPNPort := TLSALPNChallengePort + if HTTPSPort > 0 && HTTPSPort != TLSALPNChallengePort { + useTLSALPNPort = HTTPSPort + } + if am.AltTLSALPNPort > 0 { + useTLSALPNPort = am.AltTLSALPNPort + } + client.ChallengeSolvers[acme.ChallengeTypeTLSALPN01] = distributedSolver{ + acmeManager: am, + solver: &tlsALPNSolver{ + config: am.config, + address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useTLSALPNPort)), + }, + caURL: client.Directory, + } + } + } else { + // use DNS challenge exclusively + client.ChallengeSolvers[acme.ChallengeTypeDNS01] = am.DNS01Solver + } + + // register account if it is new + if account.Status == "" { + if am.NewAccountFunc != nil { + err = am.NewAccountFunc(ctx, am, account) + if err != nil { + return nil, fmt.Errorf("account pre-registration callback: %v", err) + } + } + + // agree to terms + if interactive { + if !am.Agreed { + var termsURL string + dir, err := client.GetDirectory(ctx) + if err != nil { + return nil, fmt.Errorf("getting directory: %w", err) + } + if dir.Meta != nil { + termsURL = dir.Meta.TermsOfService + } + if termsURL != "" { + am.Agreed = am.askUserAgreement(termsURL) + if !am.Agreed { + return nil, fmt.Errorf("user must agree to CA terms") + } + } + } + } else { + // can't prompt a user who isn't there; they should + // have reviewed the terms beforehand + am.Agreed = true + } + account.TermsOfServiceAgreed = am.Agreed + + // associate account with external binding, if configured + if am.ExternalAccount != nil { + err := account.SetExternalAccountBinding(ctx, client.Client, *am.ExternalAccount) + if err != nil { + return nil, err + } + } + + // create account + account, err = client.NewAccount(ctx, account) + if err != nil { + return nil, fmt.Errorf("registering account with server: %w", err) + } + + // persist the account to storage + err = am.saveAccount(caURL, account) + if err != nil { + return nil, fmt.Errorf("could not save account: %v", err) + } + } + + c := &acmeClient{ + mgr: am, + acmeClient: client, + account: account, + } + + return c, nil +} + +func (c *acmeClient) throttle(ctx context.Context, names []string) error { + // throttling is scoped to CA + account email + rateLimiterKey := c.acmeClient.Directory + "," + c.mgr.Email + rateLimitersMu.Lock() + rl, ok := rateLimiters[rateLimiterKey] + if !ok { + rl = NewRateLimiter(RateLimitEvents, RateLimitEventsWindow) + rateLimiters[rateLimiterKey] = rl + // TODO: stop rate limiter when it is garbage-collected... + } + rateLimitersMu.Unlock() + if c.mgr.Logger != nil { + c.mgr.Logger.Info("waiting on internal rate limiter", zap.Strings("identifiers", names)) + } + err := rl.Wait(ctx) + if err != nil { + return err + } + if c.mgr.Logger != nil { + c.mgr.Logger.Info("done waiting on internal rate limiter", zap.Strings("identifiers", names)) + } + return nil +} + +func (c *acmeClient) usingTestCA() bool { + return c.mgr.TestCA != "" && c.acmeClient.Directory == c.mgr.TestCA +} + +func (c *acmeClient) revoke(ctx context.Context, cert *x509.Certificate, reason int) error { + return c.acmeClient.RevokeCertificate(ctx, c.account, + cert, c.account.PrivateKey, reason) +} + +func buildUAString() string { + ua := "CertMagic" + if UserAgent != "" { + ua = UserAgent + " " + ua + } + return ua +} + +// These internal rate limits are designed to prevent accidentally +// firehosing a CA's ACME endpoints. They are not intended to +// replace or replicate the CA's actual rate limits. +// +// Let's Encrypt's rate limits can be found here: +// https://letsencrypt.org/docs/rate-limits/ +// +// Currently (as of December 2019), Let's Encrypt's most relevant +// rate limit for large deployments is 300 new orders per account +// per 3 hours (on average, or best case, that's about 1 every 36 +// seconds, or 2 every 72 seconds, etc.); but it's not reasonable +// to try to assume that our internal state is the same as the CA's +// (due to process restarts, config changes, failed validations, +// etc.) and ultimately, only the CA's actual rate limiter is the +// authority. Thus, our own rate limiters do not attempt to enforce +// external rate limits. Doing so causes problems when the domains +// are not in our control (i.e. serving customer sites) and/or lots +// of domains fail validation: they clog our internal rate limiter +// and nearly starve out (or at least slow down) the other domains +// that need certificates. Failed transactions are already retried +// with exponential backoff, so adding in rate limiting can slow +// things down even more. +// +// Instead, the point of our internal rate limiter is to avoid +// hammering the CA's endpoint when there are thousands or even +// millions of certificates under management. Our goal is to +// allow small bursts in a relatively short timeframe so as to +// not block any one domain for too long, without unleashing +// thousands of requests to the CA at once. +var ( + rateLimiters = make(map[string]*RingBufferRateLimiter) + rateLimitersMu sync.RWMutex + + // RateLimitEvents is how many new events can be allowed + // in RateLimitEventsWindow. + RateLimitEvents = 10 + + // RateLimitEventsWindow is the size of the sliding + // window that throttles events. + RateLimitEventsWindow = 1 * time.Minute +) + +// Some default values passed down to the underlying ACME client. +var ( + UserAgent string + HTTPTimeout = 30 * time.Second +) diff --git a/vendor/github.com/caddyserver/certmagic/acmemanager.go b/vendor/github.com/coolaj86/certmagic/acmemanager.go similarity index 78% rename from vendor/github.com/caddyserver/certmagic/acmemanager.go rename to vendor/github.com/coolaj86/certmagic/acmemanager.go index 8e566fa..6fa81ec 100644 --- a/vendor/github.com/caddyserver/certmagic/acmemanager.go +++ b/vendor/github.com/coolaj86/certmagic/acmemanager.go @@ -5,16 +5,14 @@ import ( "crypto/x509" "errors" "fmt" - "log" "net/http" "net/url" "strings" "time" - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/certificate" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/challenge/dns01" + "github.com/mholt/acmez" + "github.com/mholt/acmez/acme" + "go.uber.org/zap" ) // ACMEManager gets certificates using ACME. It implements the PreChecker, @@ -45,7 +43,7 @@ type ACMEManager struct { // An optional external account to associate // with this ACME account - ExternalAccount *ExternalAccountBinding + ExternalAccount *acme.EAB // Disable all HTTP challenges DisableHTTPChallenge bool @@ -70,14 +68,13 @@ type ACMEManager struct { // challenge to succeed AltTLSALPNPort int - // The DNS provider to use when solving the - // ACME DNS challenge - DNSProvider challenge.Provider + // The solver for the dns-01 challenge; + // usually this is a DNS01Solver value + // from this package + DNS01Solver acmez.Solver - // The ChallengeOption struct to provide - // custom precheck or name resolution options - // for DNS challenge validation and execution - DNSChallengeOption dns01.ChallengeOption + // The solver for the http-01 challenge + HTTP01Solver acmez.Solver // TrustedRoots specifies a pool of root CA // certificates to trust when communicating @@ -86,13 +83,27 @@ type ACMEManager struct { // The maximum amount of time to allow for // obtaining a certificate. If empty, the - // default from the underlying lego lib is + // default from the underlying ACME lib is // used. If set, it must not be too low so - // as to cancel orders too early, running - // the risk of rate limiting. + // as to cancel challenges too early. CertObtainTimeout time.Duration - config *Config + // Address of custom DNS resolver to be used + // when communicating with ACME server + Resolver string + + // Callback function that is called before a + // new ACME account is registered with the CA; + // it allows for last-second config changes + // of the ACMEManager (TODO: this feature is + // still EXPERIMENTAL and subject to change) + NewAccountFunc func(context.Context, *ACMEManager, acme.Account) error + + // Set a logger to enable logging + Logger *zap.Logger + + config *Config + httpClient *http.Client } // NewACMEManager constructs a valid ACMEManager based on a template @@ -108,7 +119,11 @@ func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager { if template.CA == "" { template.CA = DefaultACME.CA } - if template.TestCA == "" { + if template.TestCA == "" && template.CA == DefaultACME.CA { + // only use the default test CA if the CA is also + // the default CA; no point in testing against + // Let's Encrypt's staging server if we are not + // using their production server too template.TestCA = DefaultACME.TestCA } if template.Email == "" { @@ -117,6 +132,9 @@ func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager { if !template.Agreed { template.Agreed = DefaultACME.Agreed } + if template.ExternalAccount == nil { + template.ExternalAccount = DefaultACME.ExternalAccount + } if !template.DisableHTTPChallenge { template.DisableHTTPChallenge = DefaultACME.DisableHTTPChallenge } @@ -132,11 +150,8 @@ func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager { if template.AltTLSALPNPort == 0 { template.AltTLSALPNPort = DefaultACME.AltTLSALPNPort } - if template.DNSProvider == nil { - template.DNSProvider = DefaultACME.DNSProvider - } - if template.DNSChallengeOption == nil { - template.DNSChallengeOption = DefaultACME.DNSChallengeOption + if template.DNS01Solver == nil { + template.DNS01Solver = DefaultACME.DNS01Solver } if template.TrustedRoots == nil { template.TrustedRoots = DefaultACME.TrustedRoots @@ -144,6 +159,15 @@ func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager { if template.CertObtainTimeout == 0 { template.CertObtainTimeout = DefaultACME.CertObtainTimeout } + if template.Resolver == "" { + template.Resolver = DefaultACME.Resolver + } + if template.NewAccountFunc == nil { + template.NewAccountFunc = DefaultACME.NewAccountFunc + } + if template.Logger == nil { + template.Logger = DefaultACME.Logger + } template.config = cfg return &template } @@ -180,7 +204,7 @@ func (am *ACMEManager) issuerKey(ca string) string { // renewing a certificate with ACME, and returns whether this // batch is eligible for certificates if using Let's Encrypt. // It also ensures that an email address is available. -func (am *ACMEManager) PreCheck(names []string, interactive bool) error { +func (am *ACMEManager) PreCheck(_ context.Context, names []string, interactive bool) error { letsEncrypt := strings.Contains(am.CA, "api.letsencrypt.org") if letsEncrypt { for _, name := range names { @@ -240,9 +264,9 @@ func (am *ACMEManager) Issue(ctx context.Context, csr *x509.CertificateRequest) // externally; it is hard to tell which! one easy cue is whether the // error is specifically a 429 (Too Many Requests); if so, we should // probably keep retrying - var acmeErr acme.ProblemDetails - if errors.As(err, &acmeErr) { - if acmeErr.HTTPStatus == http.StatusTooManyRequests { + var problem acme.Problem + if errors.As(err, &problem) { + if problem.Status == http.StatusTooManyRequests { // DON'T abort retries; the test CA succeeded (even // if it's cached, it recently succeeded!) so we just // need to keep trying (with backoff) until this CA's @@ -261,7 +285,7 @@ func (am *ACMEManager) Issue(ctx context.Context, csr *x509.CertificateRequest) } func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest, useTestCA bool) (*IssuedCertificate, bool, error) { - client, err := am.newACMEClientWithRetry(useTestCA) + client, err := am.newACMEClient(ctx, useTestCA, false) if err != nil { return nil, false, err } @@ -275,64 +299,34 @@ func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest } } - certRes, err := client.tryAllEnabledChallenges(ctx, csr) + certChains, err := client.acmeClient.ObtainCertificateUsingCSR(ctx, client.account, csr) if err != nil { - return nil, usingTestCA, fmt.Errorf("%v %w", nameSet, err) + return nil, usingTestCA, fmt.Errorf("%v %w (ca=%s)", nameSet, err, client.acmeClient.Directory) } + // TODO: ACME server could in theory issue a cert with multiple chains, + // but we don't (yet) have a way to choose one, so just use first one ic := &IssuedCertificate{ - Certificate: certRes.Certificate, - Metadata: certRes, + Certificate: certChains[0].ChainPEM, + Metadata: certChains[0], } return ic, usingTestCA, nil } -func (c *acmeClient) tryAllEnabledChallenges(ctx context.Context, csr *x509.CertificateRequest) (*certificate.Resource, error) { - // start with all enabled challenges - challenges := c.initialChallenges() - if len(challenges) == 0 { - return nil, fmt.Errorf("no challenge types enabled") - } - - // try while a challenge type is still available - var cert *certificate.Resource - var err error - for len(challenges) > 0 { - var chosenChallenge challenge.Type - chosenChallenge, challenges = c.nextChallenge(challenges) - cert, err = c.acmeClient.Certificate.ObtainForCSR(*csr, true) - if err == nil { - return cert, nil - } - log.Printf("[ERROR] %s (challenge=%s remaining=%v)", err, chosenChallenge, challenges) - time.Sleep(2 * time.Second) - } - return cert, err -} - // Revoke implements the Revoker interface. It revokes the given certificate. -func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource) error { - client, err := am.newACMEClient(false, false) +func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource, reason int) error { + client, err := am.newACMEClient(ctx, false, false) if err != nil { return err } - meta := cert.IssuerData.(map[string]interface{}) - cr := certificate.Resource{ - Domain: meta["domain"].(string), - CertURL: meta["certUrl"].(string), - CertStableURL: meta["certStableURL"].(string), + certs, err := parseCertsFromPEMBundle(cert.CertificatePEM) + if err != nil { + return err } - return client.revoke(ctx, cr) -} - -// ExternalAccountBinding contains information for -// binding an external account to an ACME account. -type ExternalAccountBinding struct { - KeyID string - HMAC []byte + return client.revoke(ctx, certs[0], reason) } // DefaultACME specifies the default settings diff --git a/vendor/github.com/caddyserver/certmagic/async.go b/vendor/github.com/coolaj86/certmagic/async.go similarity index 76% rename from vendor/github.com/caddyserver/certmagic/async.go rename to vendor/github.com/coolaj86/certmagic/async.go index 3f9a01b..67627b2 100644 --- a/vendor/github.com/caddyserver/certmagic/async.go +++ b/vendor/github.com/coolaj86/certmagic/async.go @@ -4,8 +4,11 @@ import ( "context" "errors" "log" + "runtime" "sync" "time" + + "go.uber.org/zap" ) var jm = &jobManager{maxConcurrentJobs: 1000} @@ -19,15 +22,16 @@ type jobManager struct { } type namedJob struct { - name string - job func() error + name string + job func() error + logger *zap.Logger } // Submit enqueues the given job with the given name. If name is non-empty // and a job with the same name is already enqueued or running, this is a // no-op. If name is empty, no duplicate prevention will occur. The job // manager will then run this job as soon as it is able. -func (jm *jobManager) Submit(name string, job func() error) { +func (jm *jobManager) Submit(logger *zap.Logger, name string, job func() error) { jm.mu.Lock() defer jm.mu.Unlock() if jm.names == nil { @@ -40,7 +44,7 @@ func (jm *jobManager) Submit(name string, job func() error) { } jm.names[name] = struct{}{} } - jm.queue = append(jm.queue, namedJob{name, job}) + jm.queue = append(jm.queue, namedJob{name, job, logger}) if jm.activeWorkers < jm.maxConcurrentJobs { jm.activeWorkers++ go jm.worker() @@ -48,6 +52,14 @@ func (jm *jobManager) Submit(name string, job func() error) { } func (jm *jobManager) worker() { + defer func() { + if err := recover(); err != nil { + buf := make([]byte, stackTraceBufferSize) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("panic: certificate worker: %v\n%s", err, buf) + } + }() + for { jm.mu.Lock() if len(jm.queue) == 0 { @@ -59,7 +71,9 @@ func (jm *jobManager) worker() { jm.queue = jm.queue[1:] jm.mu.Unlock() if err := next.job(); err != nil { - log.Printf("[ERROR] %v", err) + if next.logger != nil { + next.logger.Error("job failed", zap.Error(err)) + } } if next.name != "" { jm.mu.Lock() @@ -69,7 +83,7 @@ func (jm *jobManager) worker() { } } -func doWithRetry(ctx context.Context, f func(context.Context) error) error { +func doWithRetry(ctx context.Context, log *zap.Logger, f func(context.Context) error) error { var attempts int ctx = context.WithValue(ctx, AttemptsCtxKey, &attempts) @@ -102,11 +116,22 @@ func doWithRetry(ctx context.Context, f func(context.Context) error) error { intervalIndex++ } if time.Since(start) < maxRetryDuration { - log.Printf("[ERROR] attempt %d: %v - retrying in %s (%s/%s elapsed)...", - attempts, err, retryIntervals[intervalIndex], time.Since(start), maxRetryDuration) + if log != nil { + log.Error("will retry", + zap.Error(err), + zap.Int("attempt", attempts), + zap.Duration("retrying_in", retryIntervals[intervalIndex]), + zap.Duration("elapsed", time.Since(start)), + zap.Duration("max_duration", maxRetryDuration)) + } } else { - log.Printf("[ERROR] final attempt: %v - giving up (%s/%s elapsed)...", - err, time.Since(start), maxRetryDuration) + if log != nil { + log.Error("final attempt; giving up", + zap.Error(err), + zap.Int("attempt", attempts), + zap.Duration("elapsed", time.Since(start)), + zap.Duration("max_duration", maxRetryDuration)) + } return nil } } diff --git a/vendor/github.com/caddyserver/certmagic/cache.go b/vendor/github.com/coolaj86/certmagic/cache.go similarity index 88% rename from vendor/github.com/caddyserver/certmagic/cache.go rename to vendor/github.com/coolaj86/certmagic/cache.go index bf7065a..30bfc2a 100644 --- a/vendor/github.com/caddyserver/certmagic/cache.go +++ b/vendor/github.com/coolaj86/certmagic/cache.go @@ -16,10 +16,12 @@ package certmagic import ( "fmt" - "log" + weakrand "math/rand" // seeded elsewhere "strings" "sync" "time" + + "go.uber.org/zap" ) // Cache is a structure that stores certificates in memory. @@ -62,6 +64,8 @@ type Cache struct { // Used to signal when stopping is completed doneChan chan struct{} + + logger *zap.Logger } // NewCache returns a new, valid Cache for efficiently @@ -94,6 +98,9 @@ func NewCache(opts CacheOptions) *Cache { if opts.RenewCheckInterval <= 0 { opts.RenewCheckInterval = DefaultRenewCheckInterval } + if opts.Capacity < 0 { + opts.Capacity = 0 + } // this must be set, because we cannot not // safely assume that the Default Config @@ -108,9 +115,10 @@ func NewCache(opts CacheOptions) *Cache { cacheIndex: make(map[string][]string), stopChan: make(chan struct{}), doneChan: make(chan struct{}), + logger: opts.Logger, } - go c.maintainAssets() + go c.maintainAssets(0) return c } @@ -153,6 +161,14 @@ type CacheOptions struct { // How often to check certificates for renewal; // if unset, DefaultRenewCheckInterval will be used. RenewCheckInterval time.Duration + + // Maximum number of certificates to allow in the cache. + // If reached, certificates will be randomly evicted to + // make room for new ones. 0 means unlimited. + Capacity int + + // Set a logger to enable logging + Logger *zap.Logger } // ConfigGetter is a function that returns a prepared, @@ -181,6 +197,25 @@ func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) { return } + // if the cache is at capacity, make room for new cert + cacheSize := len(certCache.cache) + if certCache.options.Capacity > 0 && cacheSize >= certCache.options.Capacity { + // Go maps are "nondeterministic" but not actually random, + // so although we could just chop off the "front" of the + // map with less code, that is a heavily skewed eviction + // strategy; generating random numbers is cheap and + // ensures a much better distribution. + rnd := weakrand.Intn(cacheSize) + i := 0 + for _, randomCert := range certCache.cache { + if i == rnd { + certCache.removeCertificate(randomCert) + break + } + i++ + } + } + // store the certificate certCache.cache[cert.hash] = cert @@ -223,16 +258,11 @@ func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) { certCache.removeCertificate(oldCert) certCache.unsyncedCacheCertificate(newCert) certCache.mu.Unlock() - log.Printf("[INFO] Replaced certificate in cache for %v (new expiration date: %s)", - newCert.Names, newCert.Leaf.NotAfter.Format("2006-01-02 15:04:05")) -} - -func (certCache *Cache) getFirstMatchingCert(name string) (Certificate, bool) { - all := certCache.getAllMatchingCerts(name) - if len(all) == 0 { - return all[0], true + if certCache.logger != nil { + certCache.logger.Info("replaced certificate in cache", + zap.Strings("identifiers", newCert.Names), + zap.Time("new_expiration", newCert.Leaf.NotAfter)) } - return Certificate{}, false } func (certCache *Cache) getAllMatchingCerts(name string) []Certificate { diff --git a/vendor/github.com/caddyserver/certmagic/certificates.go b/vendor/github.com/coolaj86/certmagic/certificates.go similarity index 86% rename from vendor/github.com/caddyserver/certmagic/certificates.go rename to vendor/github.com/coolaj86/certmagic/certificates.go index 608ca40..d1bc26b 100644 --- a/vendor/github.com/caddyserver/certmagic/certificates.go +++ b/vendor/github.com/coolaj86/certmagic/certificates.go @@ -19,11 +19,11 @@ import ( "crypto/x509" "fmt" "io/ioutil" - "log" "net" "strings" "time" + "go.uber.org/zap" "golang.org/x/crypto/ocsp" ) @@ -57,6 +57,18 @@ func (cert Certificate) NeedsRenewal(cfg *Config) bool { return currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) } +// Expired returns true if the certificate has expired. +func (cert Certificate) Expired() bool { + if cert.Leaf == nil { + // ideally cert.Leaf would never be nil, but this can happen for + // "synthetic" certs like those made to solve the TLS-ALPN challenge + // which adds a special cert directly to the cache, since + // tls.X509KeyPair() discards the leaf; oh well + return false + } + return time.Now().After(cert.Leaf.NotAfter) +} + // currentlyInRenewalWindow returns true if the current time is // within the renewal window, according to the given start/end // dates and the ratio of the renewal window. If true is returned, @@ -108,7 +120,7 @@ func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) { if err != nil { return Certificate{}, err } - cert, err := makeCertificateWithOCSP(cfg.Storage, certRes.CertificatePEM, certRes.PrivateKeyPEM) + cert, err := cfg.makeCertificateWithOCSP(certRes.CertificatePEM, certRes.PrivateKeyPEM) if err != nil { return cert, err } @@ -122,7 +134,7 @@ func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) { // // This method is safe for concurrent use. func (cfg *Config) CacheUnmanagedCertificatePEMFile(certFile, keyFile string, tags []string) error { - cert, err := makeCertificateFromDiskWithOCSP(cfg.Storage, certFile, keyFile) + cert, err := cfg.makeCertificateFromDiskWithOCSP(cfg.Storage, certFile, keyFile) if err != nil { return err } @@ -143,8 +155,8 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate, tags [] return err } _, err = stapleOCSP(cfg.Storage, &cert, nil) - if err != nil { - log.Printf("[WARNING] Stapling OCSP: %v", err) + if err != nil && cfg.Logger != nil { + cfg.Logger.Warn("stapling OCSP", zap.Error(err)) } cfg.emit("cached_unmanaged_cert", cert.Names) cert.Tags = tags @@ -157,7 +169,7 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate, tags [] // // This method is safe for concurrent use. func (cfg *Config) CacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte, tags []string) error { - cert, err := makeCertificateWithOCSP(cfg.Storage, certBytes, keyBytes) + cert, err := cfg.makeCertificateWithOCSP(certBytes, keyBytes) if err != nil { return err } @@ -171,7 +183,7 @@ func (cfg *Config) CacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte, // certificate and key files. It fills out all the fields in // the certificate except for the Managed and OnDemand flags. // (It is up to the caller to set those.) It staples OCSP. -func makeCertificateFromDiskWithOCSP(storage Storage, certFile, keyFile string) (Certificate, error) { +func (cfg Config) makeCertificateFromDiskWithOCSP(storage Storage, certFile, keyFile string) (Certificate, error) { certPEMBlock, err := ioutil.ReadFile(certFile) if err != nil { return Certificate{}, err @@ -180,19 +192,19 @@ func makeCertificateFromDiskWithOCSP(storage Storage, certFile, keyFile string) if err != nil { return Certificate{}, err } - return makeCertificateWithOCSP(storage, certPEMBlock, keyPEMBlock) + return cfg.makeCertificateWithOCSP(certPEMBlock, keyPEMBlock) } // makeCertificateWithOCSP is the same as makeCertificate except that it also // staples OCSP to the certificate. -func makeCertificateWithOCSP(storage Storage, certPEMBlock, keyPEMBlock []byte) (Certificate, error) { +func (cfg Config) makeCertificateWithOCSP(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { cert, err := makeCertificate(certPEMBlock, keyPEMBlock) if err != nil { return cert, err } - _, err = stapleOCSP(storage, &cert, certPEMBlock) - if err != nil { - log.Printf("[WARNING] Stapling OCSP: %v", err) + _, err = stapleOCSP(cfg.Storage, &cert, certPEMBlock) + if err != nil && cfg.Logger != nil { + cfg.Logger.Warn("stapling OCSP", zap.Error(err)) } return cert, nil } @@ -304,7 +316,9 @@ func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, erro // to the new cert. It assumes that the new certificate for oldCert.Names[0] is // already in storage. func (cfg *Config) reloadManagedCertificate(oldCert Certificate) error { - log.Printf("[INFO] Reloading managed certificate for %v", oldCert.Names) + if cfg.Logger != nil { + cfg.Logger.Info("reloading managed certificate", zap.Strings("identifiers", oldCert.Names)) + } newCert, err := cfg.loadManagedCertificate(oldCert.Names[0]) if err != nil { return fmt.Errorf("loading managed certificate for %v from storage: %v", oldCert.Names, err) @@ -342,32 +356,39 @@ func SubjectQualifiesForCert(subj string) bool { // allowed, as long as they conform to CABF requirements (only // one wildcard label, and it must be the left-most label). func SubjectQualifiesForPublicCert(subj string) bool { - // must at least qualify for certificate + // must at least qualify for a certificate return SubjectQualifiesForCert(subj) && - // localhost is ineligible - subj != "localhost" && + // localhost, .localhost TLD, and .local TLD are ineligible + !SubjectIsInternal(subj) && - // .localhost TLD is ineligible - !strings.HasSuffix(subj, ".localhost") && - - // .local TLD is ineligible - !strings.HasSuffix(subj, ".local") && + // cannot be an IP address (as of yet), see + // https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt + !SubjectIsIP(subj) && // only one wildcard label allowed, and it must be left-most (!strings.Contains(subj, "*") || (strings.Count(subj, "*") == 1 && len(subj) > 2 && - strings.HasPrefix(subj, "*."))) && + strings.HasPrefix(subj, "*."))) +} - // cannot be an IP address (as of yet), see - // https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt - net.ParseIP(subj) == nil +// SubjectIsIP returns true if subj is an IP address. +func SubjectIsIP(subj string) bool { + return net.ParseIP(subj) != nil +} + +// SubjectIsInternal returns true if subj is an internal-facing +// hostname or address. +func SubjectIsInternal(subj string) bool { + return subj == "localhost" || + strings.HasSuffix(subj, ".localhost") || + strings.HasSuffix(subj, ".local") } // MatchWildcard returns true if subject (a candidate DNS name) // matches wildcard (a reference DNS name), mostly according to -// RFC-compliant wildcard rules. +// RFC6125-compliant wildcard rules. func MatchWildcard(subject, wildcard string) bool { if subject == wildcard { return true diff --git a/vendor/github.com/caddyserver/certmagic/certmagic.go b/vendor/github.com/coolaj86/certmagic/certmagic.go similarity index 96% rename from vendor/github.com/caddyserver/certmagic/certmagic.go rename to vendor/github.com/coolaj86/certmagic/certmagic.go index eca4537..d2a6415 100644 --- a/vendor/github.com/caddyserver/certmagic/certmagic.go +++ b/vendor/github.com/coolaj86/certmagic/certmagic.go @@ -91,7 +91,10 @@ func HTTPS(domainNames []string, mux http.Handler) error { return err } - httpsLn, err = tls.Listen("tcp", fmt.Sprintf(":%d", HTTPSPort), cfg.TLSConfig()) + tlsConfig := cfg.TLSConfig() + tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...) + + httpsLn, err = tls.Listen("tcp", fmt.Sprintf(":%d", HTTPSPort), tlsConfig) if err != nil { httpLn.Close() httpLn = nil @@ -336,7 +339,7 @@ func hostOnly(hostport string) string { // identical calls) to Issue(), giving the issuer the option to ensure // it has all the necessary information/state. type PreChecker interface { - PreCheck(names []string, interactive bool) error + PreCheck(ctx context.Context, names []string, interactive bool) error } // Issuer is a type that can issue certificates. @@ -362,9 +365,11 @@ type Issuer interface { IssuerKey() string } -// Revoker can revoke certificates. +// Revoker can revoke certificates. Reason codes are defined +// by RFC 5280 §5.3.1: https://tools.ietf.org/html/rfc5280#section-5.3.1 +// and are available as constants in our ACME library. type Revoker interface { - Revoke(ctx context.Context, cert CertificateResource) error + Revoke(ctx context.Context, cert CertificateResource, reason int) error } // KeyGenerator can generate a private key. @@ -470,3 +475,6 @@ var ( lnMu sync.Mutex httpWg sync.WaitGroup ) + +// Maximum size for the stack trace when recovering from panics. +const stackTraceBufferSize = 1024 * 128 diff --git a/vendor/github.com/caddyserver/certmagic/config.go b/vendor/github.com/coolaj86/certmagic/config.go similarity index 89% rename from vendor/github.com/caddyserver/certmagic/config.go rename to vendor/github.com/coolaj86/certmagic/config.go index 06d9154..4cab812 100644 --- a/vendor/github.com/caddyserver/certmagic/config.go +++ b/vendor/github.com/coolaj86/certmagic/config.go @@ -24,14 +24,14 @@ import ( "crypto/x509/pkix" "encoding/asn1" "fmt" - "log" weakrand "math/rand" "net" "net/url" "strings" "time" - "github.com/go-acme/lego/v3/challenge/tlsalpn01" + "github.com/mholt/acmez" + "go.uber.org/zap" ) // Config configures a certificate manager instance. @@ -42,7 +42,7 @@ type Config struct { // renewal window, which is the span of time at the // end of the certificate's validity period in which // it should be renewed; for most certificates, the - // global default is good, but for exremely short- + // global default is good, but for extremely short- // lived certs, you may want to raise this to ~0.5. RenewalWindowRatio float64 @@ -64,8 +64,7 @@ type Config struct { // TODO: Can we call this feature "Reactive/Lazy/Passive TLS" instead? OnDemand *OnDemandConfig - // Add the must staple TLS extension to the - // CSR generated by lego/acme + // Add the must staple TLS extension to the CSR MustStaple bool // The type that issues certificates; the @@ -93,6 +92,9 @@ type Config struct { // loading TLS assets Storage Storage + // Set a logger to enable logging + Logger *zap.Logger + // required pointer to the in-memory cert cache certCache *Cache } @@ -328,7 +330,7 @@ func (cfg *Config) manageOne(ctx context.Context, domainName string, async bool) // either the old one (or sometimes the new one) is about to be // canceled. This seems like reasonable logic for any consumer // of this lib. See https://github.com/caddyserver/caddy/issues/3202 - jm.Submit("", obtain) + jm.Submit(cfg.Logger, "", obtain) return nil } return obtain() @@ -349,7 +351,7 @@ func (cfg *Config) manageOne(ctx context.Context, domainName string, async bool) } if cert.NeedsRenewal(cfg) { if async { - jm.Submit("renew_"+domainName, renew) + jm.Submit(cfg.Logger, "renew_"+domainName, renew) return nil } return renew() @@ -373,7 +375,7 @@ func (cfg *Config) ObtainCert(ctx context.Context, name string, interactive bool if cfg.storageHasCertResources(name) { return nil } - issuer, err := cfg.getPrecheckedIssuer([]string{name}, interactive) + issuer, err := cfg.getPrecheckedIssuer(ctx, []string{name}, interactive) if err != nil { return err } @@ -383,27 +385,49 @@ func (cfg *Config) ObtainCert(ctx context.Context, name string, interactive bool return cfg.obtainWithIssuer(ctx, issuer, name, interactive) } +func loggerNamed(l *zap.Logger, name string) *zap.Logger { + if l == nil { + return nil + } + return l.Named(name) +} + func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error { - log.Printf("[INFO][%s] Obtain certificate; acquiring lock...", name) + log := loggerNamed(cfg.Logger, "obtain") + + if log != nil { + log.Info("acquiring lock", zap.String("identifier", name)) + } // ensure idempotency of the obtain operation for this name lockKey := cfg.lockKey("cert_acme", name) - err := obtainLock(cfg.Storage, lockKey) + err := acquireLock(ctx, cfg.Storage, lockKey) if err != nil { return err } defer func() { - log.Printf("[INFO][%s] Obtain: Releasing lock", name) + if log != nil { + log.Info("releasing lock", zap.String("identifier", name)) + } if err := releaseLock(cfg.Storage, lockKey); err != nil { - log.Printf("[ERROR][%s] Obtain: Unable to unlock '%s': %v", name, lockKey, err) + if log != nil { + log.Error("unable to unlock", + zap.String("identifier", name), + zap.String("lock_key", lockKey), + zap.Error(err)) + } } }() - log.Printf("[INFO][%s] Obtain: Lock acquired; proceeding...", name) + if log != nil { + log.Info("lock acquired", zap.String("identifier", name)) + } f := func(ctx context.Context) error { // check if obtain is still needed -- might have been obtained during lock if cfg.storageHasCertResources(name) { - log.Printf("[INFO][%s] Obtain: Certificate already exists in storage", name) + if log != nil { + log.Info("certificate already exists in storage", zap.String("identifier", name)) + } return nil } @@ -437,29 +461,30 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str if err != nil { return fmt.Errorf("[%s] Obtain: saving assets: %v", name, err) } + + cfg.emit("cert_obtained", name) + + if log != nil { + log.Info("certificate obtained successfully", zap.String("identifier", name)) + } + return nil } + if interactive { err = f(ctx) } else { - err = doWithRetry(ctx, f) - } - if err != nil { - return err + err = doWithRetry(ctx, log, f) } - cfg.emit("cert_obtained", name) - - log.Printf("[INFO][%s] Certificate obtained successfully", name) - - return nil + return err } // RenewCert renews the certificate for name using cfg. It stows the // renewed certificate and its assets in storage if successful. It // DOES NOT update the in-memory cache with the new certificate. func (cfg *Config) RenewCert(ctx context.Context, name string, interactive bool) error { - issuer, err := cfg.getPrecheckedIssuer([]string{name}, interactive) + issuer, err := cfg.getPrecheckedIssuer(ctx, []string{name}, interactive) if err != nil { return err } @@ -470,21 +495,34 @@ func (cfg *Config) RenewCert(ctx context.Context, name string, interactive bool) } func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error { - log.Printf("[INFO][%s] Renew certificate; acquiring lock...", name) + log := loggerNamed(cfg.Logger, "renew") + + if log != nil { + log.Info("acquiring lock", zap.String("identifier", name)) + } // ensure idempotency of the renew operation for this name lockKey := cfg.lockKey("cert_acme", name) - err := obtainLock(cfg.Storage, lockKey) + err := acquireLock(ctx, cfg.Storage, lockKey) if err != nil { return err } defer func() { - log.Printf("[INFO][%s] Renew: Releasing lock", name) + if log != nil { + log.Info("releasing lock", zap.String("identifier", name)) + } if err := releaseLock(cfg.Storage, lockKey); err != nil { - log.Printf("[ERROR][%s] Renew: Unable to unlock '%s': %v", name, lockKey, err) + if log != nil { + log.Error("unable to unlock", + zap.String("identifier", name), + zap.String("lock_key", lockKey), + zap.Error(err)) + } } }() - log.Printf("[INFO][%s] Renew: Lock acquired; proceeding...", name) + if log != nil { + log.Info("lock acquired", zap.String("identifier", name)) + } f := func(ctx context.Context) error { // prepare for renewal (load PEM cert, key, and meta) @@ -496,10 +534,18 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri // check if renew is still needed - might have been renewed while waiting for lock timeLeft, needsRenew := cfg.managedCertNeedsRenewal(certRes) if !needsRenew { - log.Printf("[INFO][%s] Renew: Certificate appears to have been renewed already (expires in %s)", name, timeLeft) + if log != nil { + log.Info("certificate appears to have been renewed already", + zap.String("identifier", name), + zap.Duration("remaining", timeLeft)) + } return nil } - log.Printf("[INFO][%s] Renew: %s remaining", name, timeLeft) + if log != nil { + log.Info("renewing certificate", + zap.String("identifier", name), + zap.Duration("remaining", timeLeft)) + } privateKey, err := decodePrivateKey(certRes.PrivateKeyPEM) if err != nil { @@ -526,22 +572,23 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri if err != nil { return fmt.Errorf("[%s] Renew: saving assets: %v", name, err) } + + cfg.emit("cert_renewed", name) + + if log != nil { + log.Info("certificate renewed successfully", zap.String("identifier", name)) + } + return nil } + if interactive { err = f(ctx) } else { - err = doWithRetry(ctx, f) - } - if err != nil { - return err + err = doWithRetry(ctx, log, f) } - cfg.emit("cert_renewed", name) - - log.Printf("[INFO][%s] Certificate renewed successfully", name) - - return nil + return err } func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x509.CertificateRequest, error) { @@ -573,8 +620,8 @@ func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x5 // RevokeCert revokes the certificate for domain via ACME protocol. It requires // that cfg.Issuer is properly configured with the same issuer that issued the -// certificate being revoked. -func (cfg *Config) RevokeCert(ctx context.Context, domain string, interactive bool) error { +// certificate being revoked. See RFC 5280 §5.3.1 for reason codes. +func (cfg *Config) RevokeCert(ctx context.Context, domain string, reason int, interactive bool) error { rev := cfg.Revoker if rev == nil { rev = Default.Revoker @@ -591,7 +638,7 @@ func (cfg *Config) RevokeCert(ctx context.Context, domain string, interactive bo return fmt.Errorf("private key not found for %s", certRes.SANs) } - err = rev.Revoke(ctx, certRes) + err = rev.Revoke(ctx, certRes, reason) if err != nil { return err } @@ -632,7 +679,7 @@ func (cfg *Config) TLSConfig() *tls.Config { return &tls.Config{ // these two fields necessary for TLS-ALPN challenge GetCertificate: cfg.GetCertificate, - NextProtos: []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol}, + NextProtos: []string{acmez.ACMETLS1Protocol}, // the rest recommended for modern TLS servers MinVersion: tls.VersionTLS12, @@ -650,7 +697,7 @@ func (cfg *Config) TLSConfig() *tls.Config { // that storage is functioning. If a nil Issuer is returned // with a nil error, that means to skip this operation // (not an error, just a no-op). -func (cfg *Config) getPrecheckedIssuer(names []string, interactive bool) (Issuer, error) { +func (cfg *Config) getPrecheckedIssuer(ctx context.Context, names []string, interactive bool) (Issuer, error) { // ensure storage is writeable and readable // TODO: this is not necessary every time; should only // perform check once every so often for each storage, @@ -660,7 +707,7 @@ func (cfg *Config) getPrecheckedIssuer(names []string, interactive bool) (Issuer return nil, fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err) } if prechecker, ok := cfg.Issuer.(PreChecker); ok { - err := prechecker.PreCheck(names, interactive) + err := prechecker.PreCheck(ctx, names, interactive) if err != nil { return nil, err } @@ -686,7 +733,10 @@ func (cfg *Config) checkStorage() error { defer func() { deleteErr := cfg.Storage.Delete(key) if deleteErr != nil { - log.Printf("[ERROR] Deleting test key %s from storage: %v", key, err) + if cfg.Logger != nil { + cfg.Logger.Error("deleting test key from storage", + zap.String("key", key), zap.Error(err)) + } } // if there was no other error, make sure // to return any error returned from Delete diff --git a/vendor/github.com/caddyserver/certmagic/crypto.go b/vendor/github.com/coolaj86/certmagic/crypto.go similarity index 83% rename from vendor/github.com/caddyserver/certmagic/crypto.go rename to vendor/github.com/coolaj86/certmagic/crypto.go index 1cc12e8..2af3e62 100644 --- a/vendor/github.com/caddyserver/certmagic/crypto.go +++ b/vendor/github.com/coolaj86/certmagic/crypto.go @@ -48,7 +48,7 @@ func encodePrivateKey(key crypto.PrivateKey) ([]byte, error) { case *rsa.PrivateKey: pemType = "RSA" keyBytes = x509.MarshalPKCS1PrivateKey(key) - case *ed25519.PrivateKey: + case ed25519.PrivateKey: var err error pemType = "ED25519" keyBytes, err = x509.MarshalPKCS8PrivateKey(key) @@ -66,7 +66,7 @@ func encodePrivateKey(key crypto.PrivateKey) ([]byte, error) { // Borrowed from Go standard library, to handle various private key and PEM block types. // https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308 // https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238) -func decodePrivateKey(keyPEMBytes []byte) (crypto.PrivateKey, error) { +func decodePrivateKey(keyPEMBytes []byte) (crypto.Signer, error) { keyBlockDER, _ := pem.Decode(keyPEMBytes) if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") { @@ -80,7 +80,7 @@ func decodePrivateKey(keyPEMBytes []byte) (crypto.PrivateKey, error) { if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil { switch key := key.(type) { case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: - return key, nil + return key.(crypto.Signer), nil default: return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key) } @@ -177,6 +177,51 @@ func (cfg *Config) loadCertResource(certNamesKey string) (CertificateResource, e if err != nil { return CertificateResource{}, fmt.Errorf("decoding certificate metadata: %v", err) } + + // TODO: July 2020 - transition to new ACME lib and cert resource structure; + // for a while, we will need to convert old cert resources to new structure + certRes, err = cfg.transitionCertMetaToACMEzJuly2020Format(certRes, metaBytes) + if err != nil { + return certRes, fmt.Errorf("one-time certificate resource transition: %v", err) + } + + return certRes, nil +} + +// TODO: this is a temporary transition helper starting July 2020. +// It can go away when we think enough time has passed that most active assets have transitioned. +func (cfg *Config) transitionCertMetaToACMEzJuly2020Format(certRes CertificateResource, metaBytes []byte) (CertificateResource, error) { + data, ok := certRes.IssuerData.(map[string]interface{}) + if !ok { + return certRes, nil + } + if certURL, ok := data["url"].(string); ok && certURL != "" { + return certRes, nil + } + + var oldCertRes struct { + SANs []string `json:"sans"` + IssuerData struct { + Domain string `json:"domain"` + CertURL string `json:"certUrl"` + CertStableURL string `json:"certStableUrl"` + } `json:"issuer_data"` + } + err := json.Unmarshal(metaBytes, &oldCertRes) + if err != nil { + return certRes, fmt.Errorf("decoding into old certificate resource type: %v", err) + } + + data = map[string]interface{}{ + "url": oldCertRes.IssuerData.CertURL, + } + certRes.IssuerData = data + + err = cfg.saveCertResource(certRes) + if err != nil { + return certRes, fmt.Errorf("saving converted certificate resource: %v", err) + } + return certRes, nil } diff --git a/vendor/github.com/coolaj86/certmagic/dnsutil.go b/vendor/github.com/coolaj86/certmagic/dnsutil.go new file mode 100644 index 0000000..85f7714 --- /dev/null +++ b/vendor/github.com/coolaj86/certmagic/dnsutil.go @@ -0,0 +1,339 @@ +package certmagic + +import ( + "errors" + "fmt" + "net" + "strings" + "sync" + "time" + + "github.com/miekg/dns" +) + +// Code in this file adapted from go-acme/lego, July 2020: +// https://github.com/go-acme/lego +// by Ludovic Fernandez and Dominik Menke +// +// It has been modified. + +// findZoneByFQDN determines the zone apex for the given fqdn by recursing +// up the domain labels until the nameserver returns a SOA record in the +// answer section. +func findZoneByFQDN(fqdn string, nameservers []string) (string, error) { + if !strings.HasSuffix(fqdn, ".") { + fqdn += "." + } + soa, err := lookupSoaByFqdn(fqdn, nameservers) + if err != nil { + return "", err + } + return soa.zone, nil +} + +func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) { + if !strings.HasSuffix(fqdn, ".") { + fqdn += "." + } + + fqdnSOACacheMu.Lock() + defer fqdnSOACacheMu.Unlock() + + // prefer cached version if fresh + if ent := fqdnSOACache[fqdn]; ent != nil && !ent.isExpired() { + return ent, nil + } + + ent, err := fetchSoaByFqdn(fqdn, nameservers) + if err != nil { + return nil, err + } + + // save result to cache, but don't allow + // the cache to grow out of control + if len(fqdnSOACache) >= 1000 { + for key := range fqdnSOACache { + delete(fqdnSOACache, key) + break + } + } + fqdnSOACache[fqdn] = ent + + return ent, nil +} + +func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) { + var err error + var in *dns.Msg + + labelIndexes := dns.Split(fqdn) + for _, index := range labelIndexes { + domain := fqdn[index:] + + in, err = dnsQuery(domain, dns.TypeSOA, nameservers, true) + if err != nil { + continue + } + if in == nil { + continue + } + + switch in.Rcode { + case dns.RcodeSuccess: + // Check if we got a SOA RR in the answer section + if len(in.Answer) == 0 { + continue + } + + // CNAME records cannot/should not exist at the root of a zone. + // So we skip a domain when a CNAME is found. + if dnsMsgContainsCNAME(in) { + continue + } + + for _, ans := range in.Answer { + if soa, ok := ans.(*dns.SOA); ok { + return newSoaCacheEntry(soa), nil + } + } + case dns.RcodeNameError: + // NXDOMAIN + default: + // Any response code other than NOERROR and NXDOMAIN is treated as error + return nil, fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain) + } + } + + return nil, fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err)) +} + +// dnsMsgContainsCNAME checks for a CNAME answer in msg +func dnsMsgContainsCNAME(msg *dns.Msg) bool { + for _, ans := range msg.Answer { + if _, ok := ans.(*dns.CNAME); ok { + return true + } + } + return false +} + +func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (*dns.Msg, error) { + m := createDNSMsg(fqdn, rtype, recursive) + var in *dns.Msg + var err error + for _, ns := range nameservers { + in, err = sendDNSQuery(m, ns) + if err == nil && len(in.Answer) > 0 { + break + } + } + return in, err +} + +func createDNSMsg(fqdn string, rtype uint16, recursive bool) *dns.Msg { + m := new(dns.Msg) + m.SetQuestion(fqdn, rtype) + m.SetEdns0(4096, false) + if !recursive { + m.RecursionDesired = false + } + return m +} + +func sendDNSQuery(m *dns.Msg, ns string) (*dns.Msg, error) { + udp := &dns.Client{Net: "udp", Timeout: dnsTimeout} + in, _, err := udp.Exchange(m, ns) + // two kinds of errors we can handle by retrying with TCP: + // truncation and timeout; see https://github.com/caddyserver/caddy/issues/3639 + truncated := in != nil && in.Truncated + timeoutErr := err != nil && strings.Contains(err.Error(), "timeout") + if truncated || timeoutErr { + tcp := &dns.Client{Net: "tcp", Timeout: dnsTimeout} + in, _, err = tcp.Exchange(m, ns) + } + return in, err +} + +func formatDNSError(msg *dns.Msg, err error) string { + var parts []string + if msg != nil { + parts = append(parts, dns.RcodeToString[msg.Rcode]) + } + if err != nil { + parts = append(parts, err.Error()) + } + if len(parts) > 0 { + return ": " + strings.Join(parts, " ") + } + return "" +} + +// soaCacheEntry holds a cached SOA record (only selected fields) +type soaCacheEntry struct { + zone string // zone apex (a domain name) + primaryNs string // primary nameserver for the zone apex + expires time.Time // time when this cache entry should be evicted +} + +func newSoaCacheEntry(soa *dns.SOA) *soaCacheEntry { + return &soaCacheEntry{ + zone: soa.Hdr.Name, + primaryNs: soa.Ns, + expires: time.Now().Add(time.Duration(soa.Refresh) * time.Second), + } +} + +// isExpired checks whether a cache entry should be considered expired. +func (cache *soaCacheEntry) isExpired() bool { + return time.Now().After(cache.expires) +} + +// systemOrDefaultNameservers attempts to get system nameservers from the +// resolv.conf file given by path before falling back to hard-coded defaults. +func systemOrDefaultNameservers(path string, defaults []string) []string { + config, err := dns.ClientConfigFromFile(path) + if err != nil || len(config.Servers) == 0 { + return defaults + } + return config.Servers +} + +// populateNameserverPorts ensures that all nameservers have a port number. +func populateNameserverPorts(servers []string) { + for i := range servers { + _, port, _ := net.SplitHostPort(servers[i]) + if port == "" { + servers[i] = net.JoinHostPort(servers[i], "53") + } + } +} + +// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers. +func checkDNSPropagation(fqdn, value string, resolvers []string) (bool, error) { + if !strings.HasSuffix(fqdn, ".") { + fqdn += "." + } + + // Initial attempt to resolve at the recursive NS + r, err := dnsQuery(fqdn, dns.TypeTXT, resolvers, true) + if err != nil { + return false, err + } + + // TODO: make this configurable, maybe + // if !p.requireCompletePropagation { + // return true, nil + // } + + if r.Rcode == dns.RcodeSuccess { + fqdn = updateDomainWithCName(r, fqdn) + } + + authoritativeNss, err := lookupNameservers(fqdn, resolvers) + if err != nil { + return false, err + } + + return checkAuthoritativeNss(fqdn, value, authoritativeNss) +} + +// checkAuthoritativeNss queries each of the given nameservers for the expected TXT record. +func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, error) { + for _, ns := range nameservers { + r, err := dnsQuery(fqdn, dns.TypeTXT, []string{net.JoinHostPort(ns, "53")}, false) + if err != nil { + return false, err + } + + if r.Rcode != dns.RcodeSuccess { + if r.Rcode == dns.RcodeNameError { + // if Present() succeeded, then it must show up eventually, or else + // something is really broken in the DNS provider or their API; + // no need for error here, simply have the caller try again + return false, nil + } + return false, fmt.Errorf("NS %s returned %s for %s", ns, dns.RcodeToString[r.Rcode], fqdn) + } + + var found bool + for _, rr := range r.Answer { + if txt, ok := rr.(*dns.TXT); ok { + record := strings.Join(txt.Txt, "") + if record == value { + found = true + break + } + } + } + + if !found { + return false, nil + } + } + + return true, nil +} + +// lookupNameservers returns the authoritative nameservers for the given fqdn. +func lookupNameservers(fqdn string, resolvers []string) ([]string, error) { + var authoritativeNss []string + + zone, err := findZoneByFQDN(fqdn, resolvers) + if err != nil { + return nil, fmt.Errorf("could not determine the zone: %w", err) + } + + r, err := dnsQuery(zone, dns.TypeNS, resolvers, true) + if err != nil { + return nil, err + } + + for _, rr := range r.Answer { + if ns, ok := rr.(*dns.NS); ok { + authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns)) + } + } + + if len(authoritativeNss) > 0 { + return authoritativeNss, nil + } + return nil, errors.New("could not determine authoritative nameservers") +} + +// Update FQDN with CNAME if any +func updateDomainWithCName(r *dns.Msg, fqdn string) string { + for _, rr := range r.Answer { + if cn, ok := rr.(*dns.CNAME); ok { + if cn.Hdr.Name == fqdn { + return cn.Target + } + } + } + return fqdn +} + +// recursiveNameservers are used to pre-check DNS propagation. It +// prepends user-configured nameservers (custom) to the defaults +// obtained from resolv.conf and defaultNameservers and ensures +// that all server addresses have a port value. +func recursiveNameservers(custom []string) []string { + servers := append(custom, systemOrDefaultNameservers(defaultResolvConf, defaultNameservers)...) + populateNameserverPorts(servers) + return servers +} + +var defaultNameservers = []string{ + "8.8.8.8:53", + "8.8.4.4:53", + "1.1.1.1:53", + "1.0.0.1:53", +} + +var dnsTimeout = 10 * time.Second + +var ( + fqdnSOACache = map[string]*soaCacheEntry{} + fqdnSOACacheMu sync.Mutex +) + +const defaultResolvConf = "/etc/resolv.conf" diff --git a/vendor/github.com/caddyserver/certmagic/filestorage.go b/vendor/github.com/coolaj86/certmagic/filestorage.go similarity index 95% rename from vendor/github.com/caddyserver/certmagic/filestorage.go rename to vendor/github.com/coolaj86/certmagic/filestorage.go index 6887d81..f3603d0 100644 --- a/vendor/github.com/caddyserver/certmagic/filestorage.go +++ b/vendor/github.com/coolaj86/certmagic/filestorage.go @@ -15,6 +15,7 @@ package certmagic import ( + "context" "encoding/json" "fmt" "io" @@ -124,7 +125,7 @@ func (fs *FileStorage) Filename(key string) string { // Lock obtains a lock named by the given key. It blocks // until the lock can be obtained or an error is returned. -func (fs *FileStorage) Lock(key string) error { +func (fs *FileStorage) Lock(ctx context.Context, key string) error { filename := fs.lockFilename(key) for { @@ -168,8 +169,13 @@ func (fs *FileStorage) Lock(key string) error { default: // lockfile exists and is not stale; - // just wait a moment and try again - time.Sleep(fileLockPollInterval) + // just wait a moment and try again, + // or return if context cancelled + select { + case <-time.After(fileLockPollInterval): + case <-ctx.Done(): + return ctx.Err() + } } } } @@ -246,6 +252,14 @@ func removeLockfile(filename string) error { // not terminate until up to lockFreshnessInterval after // the lock is released. func keepLockfileFresh(filename string) { + defer func() { + if err := recover(); err != nil { + buf := make([]byte, stackTraceBufferSize) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("panic: active locking: %v\n%s", err, buf) + } + }() + for { time.Sleep(lockFreshnessInterval) done, err := updateLockfileFreshness(filename) diff --git a/vendor/github.com/coolaj86/certmagic/go.mod b/vendor/github.com/coolaj86/certmagic/go.mod new file mode 100644 index 0000000..c02fd0a --- /dev/null +++ b/vendor/github.com/coolaj86/certmagic/go.mod @@ -0,0 +1,12 @@ +module github.com/coolaj86/certmagic + +go 1.14 + +require ( + github.com/klauspost/cpuid v1.2.5 + github.com/libdns/libdns v0.1.0 + github.com/mholt/acmez v0.1.1 + github.com/miekg/dns v1.1.30 + go.uber.org/zap v1.15.0 + golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de +) diff --git a/vendor/github.com/coolaj86/certmagic/go.sum b/vendor/github.com/coolaj86/certmagic/go.sum new file mode 100644 index 0000000..ccae064 --- /dev/null +++ b/vendor/github.com/coolaj86/certmagic/go.sum @@ -0,0 +1,82 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid v1.2.5 h1:VBd9MyVIiJHzzgnrLQG5Bcv75H4YaWrlKqWHjurxCGo= +github.com/klauspost/cpuid v1.2.5/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04= +github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= +github.com/mholt/acmez v0.1.1 h1:KQODCqk+hBn3O7qfCRPj6L96uG65T5BSS95FKNEqtdA= +github.com/mholt/acmez v0.1.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= +github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo= +github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/vendor/github.com/caddyserver/certmagic/handshake.go b/vendor/github.com/coolaj86/certmagic/handshake.go similarity index 84% rename from vendor/github.com/caddyserver/certmagic/handshake.go rename to vendor/github.com/coolaj86/certmagic/handshake.go index e3fd436..4c3228a 100644 --- a/vendor/github.com/caddyserver/certmagic/handshake.go +++ b/vendor/github.com/coolaj86/certmagic/handshake.go @@ -19,13 +19,14 @@ import ( "crypto/tls" "encoding/json" "fmt" - "log" "net" "strings" "sync" "time" - "github.com/go-acme/lego/v3/challenge/tlsalpn01" + "github.com/mholt/acmez" + "github.com/mholt/acmez/acme" + "go.uber.org/zap" ) // GetCertificate gets a certificate to satisfy clientHello. In getting @@ -42,7 +43,7 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif // special case: serve up the certificate for a TLS-ALPN ACME challenge // (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05) for _, proto := range clientHello.SupportedProtos { - if proto == tlsalpn01.ACMETLS1Protocol { + if proto == acmez.ACMETLS1Protocol { cfg.certCache.mu.RLock() challengeCert, ok := cfg.certCache.cache[tlsALPNCertKeyName(clientHello.ServerName)] cfg.certCache.mu.RUnlock() @@ -53,15 +54,30 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif // should already have taken care of that when we made the tls.Config) challengeCert, ok, err := cfg.tryDistributedChallengeSolver(clientHello) if err != nil { - log.Printf("[ERROR][%s] TLS-ALPN challenge: %v", clientHello.ServerName, err) + if cfg.Logger != nil { + cfg.Logger.Error("tls-alpn challenge", + zap.String("server_name", clientHello.ServerName), + zap.Error(err)) + } } if ok { - log.Printf("[INFO][%s] Served key authentication certificate (distributed TLS-ALPN challenge)", clientHello.ServerName) + if cfg.Logger != nil { + cfg.Logger.Info("served key authentication certificate", + zap.String("server_name", clientHello.ServerName), + zap.String("challenge", "tls-alpn-01"), + zap.String("remote", clientHello.Conn.RemoteAddr().String()), + zap.Bool("distributed", true)) + } return &challengeCert.Certificate, nil } return nil, fmt.Errorf("no certificate to complete TLS-ALPN challenge for SNI name: %s", clientHello.ServerName) } - log.Printf("[INFO][%s] Served key authentication certificate (TLS-ALPN challenge)", clientHello.ServerName) + if cfg.Logger != nil { + cfg.Logger.Info("served key authentication certificate", + zap.String("server_name", clientHello.ServerName), + zap.String("challenge", "tls-alpn-01"), + zap.String("remote", clientHello.Conn.RemoteAddr().String())) + } return &challengeCert.Certificate, nil } } @@ -221,9 +237,19 @@ func DefaultCertificateSelector(hello *tls.ClientHelloInfo, choices []Certificat // // This function is safe for concurrent use. func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) { + log := loggerNamed(cfg.Logger, "on_demand") + // First check our in-memory cache to see if we've already loaded it cert, matched, defaulted := cfg.getCertificate(hello) if matched { + if cert.managed && cfg.OnDemand != nil && obtainIfNecessary { + // It's been reported before that if the machine goes to sleep (or + // suspends the process) that certs which are already loaded into + // memory won't get renewed in the background, so we need to check + // expiry on each handshake too, sigh: + // https://caddy.community/t/local-certificates-not-renewing-on-demand/9482 + return cfg.optionalMaintenance(log, cert, hello) + } return cert, nil } @@ -237,7 +263,11 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece if err == nil { loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert) if err != nil { - log.Printf("[ERROR] Maintaining newly-loaded certificate for %s: %v", name, err) + if log != nil { + log.Error("maintining newly-loaded certificate", + zap.String("server_name", name), + zap.Error(err)) + } } return loadedCert, nil } @@ -263,6 +293,30 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece return Certificate{}, fmt.Errorf("no certificate available for '%s'", name) } +// optionalMaintenance will perform maintenance on the certificate (if necessary) and +// will return the resulting certificate. This should only be done if the certificate +// is managed, OnDemand is enabled, and the scope is allowed to obtain certificates. +func (cfg *Config) optionalMaintenance(log *zap.Logger, cert Certificate, hello *tls.ClientHelloInfo) (Certificate, error) { + newCert, err := cfg.handshakeMaintenance(hello, cert) + if err == nil { + return newCert, nil + } + + if log != nil { + log.Error("renewing certificate on-demand failed", + zap.Strings("subjects", cert.Names), + zap.Time("not_after", cert.Leaf.NotAfter), + zap.Error(err)) + } + + if cert.Expired() { + return cert, err + } + + // still has time remaining, so serve it anyway + return cert, nil +} + // checkIfCertShouldBeObtained checks to see if an on-demand TLS certificate // should be obtained for a given domain based upon the config settings. If // a non-nil error is returned, do not issue a new certificate for name. @@ -289,6 +343,8 @@ func (cfg *Config) checkIfCertShouldBeObtained(name string) error { // // This function is safe for use by multiple concurrent goroutines. func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certificate, error) { + log := loggerNamed(cfg.Logger, "on_demand") + name := cfg.getNameFromClientHello(hello) // We must protect this process from happening concurrently, so synchronize. @@ -309,7 +365,9 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif obtainCertWaitChansMu.Unlock() // obtain the certificate - log.Printf("[INFO] Obtaining new certificate for %s", name) + if log != nil { + log.Info("obtaining new certificate", zap.String("server_name", name)) + } // TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second) defer cancel() @@ -334,13 +392,21 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif } // handshakeMaintenance performs a check on cert for expiration and OCSP validity. +// If necessary, it will renew the certificate and/or refresh the OCSP staple. +// OCSP stapling errors are not returned, only logged. // // This function is safe for use by multiple concurrent goroutines. func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certificate) (Certificate, error) { + log := loggerNamed(cfg.Logger, "on_demand") + // Check cert expiration timeLeft := cert.Leaf.NotAfter.Sub(time.Now().UTC()) if currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) { - log.Printf("[INFO] Certificate for %v expires in %s; attempting renewal", cert.Names, timeLeft) + if log != nil { + log.Info("certificate expires soon; attempting renewal", + zap.Strings("identifiers", cert.Names), + zap.Duration("remaining", timeLeft)) + } return cfg.renewDynamicCertificate(hello, cert) } @@ -352,7 +418,11 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi if err != nil { // An error with OCSP stapling is not the end of the world, and in fact, is // quite common considering not all certs have issuer URLs that support it. - log.Printf("[ERROR] Getting OCSP for %s: %v", hello.ServerName, err) + if log != nil { + log.Warn("stapling OCSP", + zap.String("server_name", hello.ServerName), + zap.Error(err)) + } } cfg.certCache.mu.Lock() cfg.certCache.cache[cert.hash] = cert @@ -370,6 +440,8 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi // // This function is safe for use by multiple concurrent goroutines. func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) { + log := loggerNamed(cfg.Logger, "on_demand") + name := cfg.getNameFromClientHello(hello) obtainCertWaitChansMu.Lock() @@ -398,7 +470,9 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe } // renew and reload the certificate - log.Printf("[INFO] Renewing certificate for %s", name) + if log != nil { + log.Info("renewing certificate", zap.String("server_name", name)) + } // TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second) defer cancel() @@ -409,7 +483,9 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe // make the replacement as atomic as possible. newCert, err := cfg.CacheManagedCertificate(name) if err != nil { - log.Printf("[ERROR] loading renewed certificate for %s: %v", name, err) + if log != nil { + log.Error("loading renewed certificate", zap.String("server_name", name), zap.Error(err)) + } } else { // replace the old certificate with the new one cfg.certCache.replaceCertificate(currentCert, newCert) @@ -451,13 +527,13 @@ func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInf return Certificate{}, false, fmt.Errorf("opening distributed challenge token file %s: %v", tokenKey, err) } - var chalInfo challengeInfo + var chalInfo acme.Challenge err = json.Unmarshal(chalInfoBytes, &chalInfo) if err != nil { return Certificate{}, false, fmt.Errorf("decoding challenge token file %s (corrupted?): %v", tokenKey, err) } - cert, err := tlsalpn01.ChallengeCert(chalInfo.Domain, chalInfo.KeyAuth) + cert, err := acmez.TLSALPN01ChallengeCert(chalInfo) if err != nil { return Certificate{}, false, fmt.Errorf("making TLS-ALPN challenge certificate: %v", err) } diff --git a/vendor/github.com/caddyserver/certmagic/httphandler.go b/vendor/github.com/coolaj86/certmagic/httphandler.go similarity index 75% rename from vendor/github.com/caddyserver/certmagic/httphandler.go rename to vendor/github.com/coolaj86/certmagic/httphandler.go index 856e723..e389721 100644 --- a/vendor/github.com/caddyserver/certmagic/httphandler.go +++ b/vendor/github.com/coolaj86/certmagic/httphandler.go @@ -16,11 +16,11 @@ package certmagic import ( "encoding/json" - "log" "net/http" "strings" - "github.com/go-acme/lego/v3/challenge/http01" + "github.com/mholt/acmez/acme" + "go.uber.org/zap" ) // HTTPChallengeHandler wraps h in a handler that can solve the ACME @@ -78,33 +78,47 @@ func (am *ACMEManager) distributedHTTPChallengeSolver(w http.ResponseWriter, r * chalInfoBytes, err := am.config.Storage.Load(tokenKey) if err != nil { if _, ok := err.(ErrNotExist); !ok { - log.Printf("[ERROR][%s] Opening distributed HTTP challenge token file: %v", host, err) + if am.Logger != nil { + am.Logger.Error("opening distributed HTTP challenge token file", + zap.String("host", host), + zap.Error(err)) + } } return false } - var chalInfo challengeInfo - err = json.Unmarshal(chalInfoBytes, &chalInfo) + var challenge acme.Challenge + err = json.Unmarshal(chalInfoBytes, &challenge) if err != nil { - log.Printf("[ERROR][%s] Decoding challenge token file %s (corrupted?): %v", host, tokenKey, err) + if am.Logger != nil { + am.Logger.Error("decoding HTTP challenge token file (corrupted?)", + zap.String("host", host), + zap.String("token_key", tokenKey), + zap.Error(err)) + } return false } - return answerHTTPChallenge(w, r, chalInfo) + return am.answerHTTPChallenge(w, r, challenge) } // answerHTTPChallenge solves the challenge with chalInfo. -// Most of this code borrowed from xenolf/lego's built-in HTTP-01 +// Most of this code borrowed from xenolf's built-in HTTP-01 // challenge solver in March 2018. -func answerHTTPChallenge(w http.ResponseWriter, r *http.Request, chalInfo challengeInfo) bool { - challengeReqPath := http01.ChallengePath(chalInfo.Token) +func (am *ACMEManager) answerHTTPChallenge(w http.ResponseWriter, r *http.Request, challenge acme.Challenge) bool { + challengeReqPath := challenge.HTTP01ResourcePath() if r.URL.Path == challengeReqPath && - strings.EqualFold(hostOnly(r.Host), chalInfo.Domain) && // mitigate DNS rebinding attacks + strings.EqualFold(hostOnly(r.Host), challenge.Identifier.Value) && // mitigate DNS rebinding attacks r.Method == "GET" { w.Header().Add("Content-Type", "text/plain") - w.Write([]byte(chalInfo.KeyAuth)) + w.Write([]byte(challenge.KeyAuthorization)) r.Close = true - log.Printf("[INFO][%s] Served key authentication (HTTP challenge)", chalInfo.Domain) + if am.Logger != nil { + am.Logger.Info("served key authentication", + zap.String("identifier", challenge.Identifier.Value), + zap.String("challenge", "http-01"), + zap.String("remote", r.RemoteAddr)) + } return true } return false diff --git a/vendor/github.com/caddyserver/certmagic/maintain.go b/vendor/github.com/coolaj86/certmagic/maintain.go similarity index 74% rename from vendor/github.com/caddyserver/certmagic/maintain.go rename to vendor/github.com/coolaj86/certmagic/maintain.go index 8974193..0e7acc3 100644 --- a/vendor/github.com/caddyserver/certmagic/maintain.go +++ b/vendor/github.com/coolaj86/certmagic/maintain.go @@ -21,9 +21,11 @@ import ( "fmt" "log" "path" + "runtime" "strings" "time" + "go.uber.org/zap" "golang.org/x/crypto/ocsp" ) @@ -31,12 +33,35 @@ import ( // that loops indefinitely and, on a regular schedule, checks // certificates for expiration and initiates a renewal of certs // that are expiring soon. It also updates OCSP stapling. It -// should only be called once per cache. -func (certCache *Cache) maintainAssets() { +// should only be called once per cache. Panics are recovered, +// and if panicCount < 10, the function is called recursively, +// incrementing panicCount each time. Initial invocation should +// start panicCount at 0. +func (certCache *Cache) maintainAssets(panicCount int) { + log := loggerNamed(certCache.logger, "maintenance") + if log != nil { + log = log.With(zap.String("cache", fmt.Sprintf("%p", certCache))) + } + + defer func() { + if err := recover(); err != nil { + buf := make([]byte, stackTraceBufferSize) + buf = buf[:runtime.Stack(buf, false)] + if log != nil { + log.Error("panic", zap.Any("error", err), zap.ByteString("stack", buf)) + } + if panicCount < 10 { + certCache.maintainAssets(panicCount + 1) + } + } + }() + renewalTicker := time.NewTicker(certCache.options.RenewCheckInterval) ocspTicker := time.NewTicker(certCache.options.OCSPCheckInterval) - log.Printf("[INFO][cache:%p] Started certificate maintenance routine", certCache) + if log != nil { + log.Info("started background certificate maintenance") + } ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -45,8 +70,8 @@ func (certCache *Cache) maintainAssets() { select { case <-renewalTicker.C: err := certCache.RenewManagedCertificates(ctx) - if err != nil { - log.Printf("[ERROR][cache:%p] Renewing managed certificates: %v", certCache, err) + if err != nil && log != nil { + log.Error("renewing managed certificates", zap.Error(err)) } case <-ocspTicker.C: certCache.updateOCSPStaples(ctx) @@ -54,7 +79,9 @@ func (certCache *Cache) maintainAssets() { renewalTicker.Stop() ocspTicker.Stop() // TODO: stop any in-progress maintenance operations and clear locks we made (this might be done now with our use of context) - log.Printf("[INFO][cache:%p] Stopped certificate maintenance routine", certCache) + if log != nil { + log.Info("stopped background certificate maintenance") + } close(certCache.doneChan) return } @@ -67,6 +94,8 @@ func (certCache *Cache) maintainAssets() { // need to call this. This method assumes non-interactive // mode (i.e. operating in the background). func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { + log := loggerNamed(certCache.logger, "maintenance") + // configs will hold a map of certificate name to the config // to use when managing that certificate configs := make(map[string]*Config) @@ -87,7 +116,9 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { // the list of names on this cert should never be empty... programmer error? if cert.Names == nil || len(cert.Names) == 0 { - log.Printf("[WARNING] Certificate keyed by '%s' has no names: %v - removing from cache", certKey, cert.Names) + if log != nil { + log.Warn("certificate has no names; removing from cache", zap.String("cert_key", certKey)) + } deleteQueue = append(deleteQueue, cert) continue } @@ -95,12 +126,19 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { // get the config associated with this certificate cfg, err := certCache.getConfig(cert) if err != nil { - log.Printf("[ERROR] Getting configuration to manage certificate for names %v; unable to renew: %v", cert.Names, err) + if log != nil { + log.Error("unable to get configuration to manage certificate; unable to renew", + zap.Strings("identifiers", cert.Names), + zap.Error(err)) + } continue } if cfg == nil { // this is bad if this happens, probably a programmer error (oops) - log.Printf("[ERROR] No configuration associated with certificate for names %v; unable to manage", cert.Names) + if log != nil { + log.Error("no configuration associated with certificate; unable to manage", + zap.Strings("identifiers", cert.Names)) + } continue } @@ -115,8 +153,11 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { storedCertExpiring, err := cfg.managedCertInStorageExpiresSoon(cert) if err != nil { // hmm, weird, but not a big deal, maybe it was deleted or something - log.Printf("[NOTICE] Error while checking if certificate for %v in storage is also expiring soon: %v", - cert.Names, err) + if log != nil { + log.Warn("error while checking if stored certificate is also expiring soon", + zap.Strings("identifiers", cert.Names), + zap.Error(err)) + } } else if !storedCertExpiring { // if the certificate is NOT expiring soon and there was no error, then we // are good to just reload the certificate from storage instead of repeating @@ -137,15 +178,22 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { // Reload certificates that merely need to be updated in memory for _, oldCert := range reloadQueue { timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO] %v Maintenance routine: certificate expires in %s, but is already renewed in storage; reloading stored certificate", - oldCert.Names, timeLeft) + if log != nil { + log.Info("certificate expires soon, but is already renewed in storage; reloading stored certificate", + zap.Strings("identifiers", oldCert.Names), + zap.Duration("remaining", timeLeft)) + } cfg := configs[oldCert.Names[0]] // crucially, this happens OUTSIDE a lock on the certCache err := cfg.reloadManagedCertificate(oldCert) if err != nil { - log.Printf("[ERROR] Loading renewed certificate: %v", err) + if log != nil { + log.Error("loading renewed certificate", + zap.Strings("identifiers", oldCert.Names), + zap.Error(err)) + } continue } } @@ -155,7 +203,11 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { cfg := configs[oldCert.Names[0]] err := certCache.queueRenewalTask(ctx, oldCert, cfg) if err != nil { - log.Printf("[ERROR] %v", err) + if log != nil { + log.Error("queueing renewal task", + zap.Strings("identifiers", oldCert.Names), + zap.Error(err)) + } continue } } @@ -171,8 +223,14 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { } func (certCache *Cache) queueRenewalTask(ctx context.Context, oldCert Certificate, cfg *Config) error { + log := loggerNamed(certCache.logger, "maintenance") + timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO] %v Maintenance routine: certificate expires in %v; queueing for renewal", oldCert.Names, timeLeft) + if log != nil { + log.Info("certificate expires soon; queuing for renewal", + zap.Strings("identifiers", oldCert.Names), + zap.Duration("remaining", timeLeft)) + } // Get the name which we should use to renew this certificate; // we only support managing certificates with one name per cert, @@ -180,9 +238,13 @@ func (certCache *Cache) queueRenewalTask(ctx context.Context, oldCert Certificat renewName := oldCert.Names[0] // queue up this renewal job (is a no-op if already active or queued) - jm.Submit("renew_"+renewName, func() error { + jm.Submit(cfg.Logger, "renew_"+renewName, func() error { timeLeft := oldCert.Leaf.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO] %v Maintenance routine: attempting renewal with %v remaining", oldCert.Names, timeLeft) + if log != nil { + log.Info("attempting certificate renewal", + zap.Strings("identifiers", oldCert.Names), + zap.Duration("remaining", timeLeft)) + } // perform renewal - crucially, this happens OUTSIDE a lock on certCache err := cfg.RenewCert(ctx, renewName, false) @@ -215,6 +277,8 @@ func (certCache *Cache) queueRenewalTask(ctx context.Context, oldCert Certificat // Ryan Sleevi's recommendations for good OCSP support: // https://gist.github.com/sleevi/5efe9ef98961ecfb4da8 func (certCache *Cache) updateOCSPStaples(ctx context.Context) { + log := loggerNamed(certCache.logger, "maintenance") + // temporary structures to store updates or tasks // so that we can keep our locks short-lived type ocspUpdate struct { @@ -234,8 +298,8 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { // obtain brief read lock during our scan to see which staples need updating certCache.mu.RLock() for certHash, cert := range certCache.cache { - // no point in updating OCSP for expired certificates - if time.Now().After(cert.Leaf.NotAfter) { + // no point in updating OCSP for expired or "synthetic" certificates + if cert.Leaf == nil || cert.Expired() { continue } var lastNextUpdate time.Time @@ -257,12 +321,19 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { cfg, err := certCache.getConfig(cert) if err != nil { - log.Printf("[ERROR] Getting configuration to manage OCSP for certificate with names %v; unable to refresh: %v", cert.Names, err) + if log != nil { + log.Error("unable to refresh OCSP staple because getting automation config for certificate failed", + zap.Strings("identifiers", cert.Names), + zap.Error(err)) + } continue } if cfg == nil { // this is bad if this happens, probably a programmer error (oops) - log.Printf("[ERROR] No configuration associated with certificate for names %v; unable to manage OCSP", cert.Names) + if log != nil { + log.Error("no configuration associated with certificate; unable to manage OCSP staples", + zap.Strings("identifiers", cert.Names)) + } continue } @@ -270,7 +341,11 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { if err != nil { if cert.ocsp != nil { // if there was no staple before, that's fine; otherwise we should log the error - log.Printf("[ERROR] Checking OCSP: %v", err) + if log != nil { + log.Error("stapling OCSP", + zap.Strings("identifiers", cert.Names), + zap.Error(err)) + } } continue } @@ -279,8 +354,12 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { // If there was no staple before, or if the response is updated, make // sure we apply the update to all names on the certificate. if cert.ocsp != nil && (lastNextUpdate.IsZero() || lastNextUpdate != cert.ocsp.NextUpdate) { - log.Printf("[INFO] Advancing OCSP staple for %v from %s to %s", - cert.Names, lastNextUpdate, cert.ocsp.NextUpdate) + if log != nil { + log.Info("advancing OCSP staple", + zap.Strings("identifiers", cert.Names), + zap.Time("from", lastNextUpdate), + zap.Time("to", cert.ocsp.NextUpdate)) + } updated[certHash] = ocspUpdate{rawBytes: cert.Certificate.OCSPStaple, parsed: cert.ocsp} } @@ -305,8 +384,11 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { // We attempt to replace any certificates that were revoked. // Crucially, this happens OUTSIDE a lock on the certCache. for _, oldCert := range renewQueue { - log.Printf("[INFO] OCSP status for managed certificate %v (expiration=%s) is REVOKED; attempting to replace with new certificate", - oldCert.Names, oldCert.Leaf.NotAfter) + if log != nil { + log.Warn("OCSP status for managed certificate is REVOKED; attempting to replace with new certificate", + zap.Strings("identifiers", oldCert.Names), + zap.Time("expiration", oldCert.Leaf.NotAfter)) + } renewName := oldCert.Names[0] cfg := configs[renewName] @@ -315,7 +397,11 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { err := cfg.RenewCert(ctx, renewName, false) if err != nil { // probably better to not serve a revoked certificate at all - log.Printf("[ERROR] Obtaining new certificate for %v due to OCSP status of revoked: %v; removing from cache", oldCert.Names, err) + if log != nil { + log.Error("unable to obtain new to certificate after OCSP status of REVOKED; removing from cache", + zap.Strings("identifiers", oldCert.Names), + zap.Error(err)) + } certCache.mu.Lock() certCache.removeCertificate(oldCert) certCache.mu.Unlock() @@ -323,7 +409,11 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { } err = cfg.reloadManagedCertificate(oldCert) if err != nil { - log.Printf("[ERROR] After obtaining new certificate due to OCSP status of revoked: %v", err) + if log != nil { + log.Error("after obtaining new certificate due to OCSP status of REVOKED", + zap.Strings("identifiers", oldCert.Names), + zap.Error(err)) + } continue } } @@ -338,15 +428,15 @@ type CleanStorageOptions struct { // CleanStorage removes assets which are no longer useful, // according to opts. -func CleanStorage(storage Storage, opts CleanStorageOptions) { +func CleanStorage(ctx context.Context, storage Storage, opts CleanStorageOptions) { if opts.OCSPStaples { - err := deleteOldOCSPStaples(storage) + err := deleteOldOCSPStaples(ctx, storage) if err != nil { log.Printf("[ERROR] Deleting old OCSP staples: %v", err) } } if opts.ExpiredCerts { - err := deleteExpiredCerts(storage, opts.ExpiredCertGracePeriod) + err := deleteExpiredCerts(ctx, storage, opts.ExpiredCertGracePeriod) if err != nil { log.Printf("[ERROR] Deleting expired certificates: %v", err) } @@ -354,13 +444,19 @@ func CleanStorage(storage Storage, opts CleanStorageOptions) { // TODO: delete stale locks? } -func deleteOldOCSPStaples(storage Storage) error { +func deleteOldOCSPStaples(ctx context.Context, storage Storage) error { ocspKeys, err := storage.List(prefixOCSP, false) if err != nil { // maybe just hasn't been created yet; no big deal return nil } for _, key := range ocspKeys { + // if context was cancelled, quit early; otherwise proceed + select { + case <-ctx.Done(): + return ctx.Err() + default: + } ocspBytes, err := storage.Load(key) if err != nil { log.Printf("[ERROR] While deleting old OCSP staples, unable to load staple file: %v", err) @@ -386,7 +482,7 @@ func deleteOldOCSPStaples(storage Storage) error { return nil } -func deleteExpiredCerts(storage Storage, gracePeriod time.Duration) error { +func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.Duration) error { issuerKeys, err := storage.List(prefixCerts, false) if err != nil { // maybe just hasn't been created yet; no big deal @@ -401,6 +497,13 @@ func deleteExpiredCerts(storage Storage, gracePeriod time.Duration) error { } for _, siteKey := range siteKeys { + // if context was cancelled, quit early; otherwise proceed + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + siteAssets, err := storage.List(siteKey, false) if err != nil { log.Printf("[ERROR] Listing contents of %s: %v", siteKey, err) diff --git a/vendor/github.com/caddyserver/certmagic/ocsp.go b/vendor/github.com/coolaj86/certmagic/ocsp.go similarity index 99% rename from vendor/github.com/caddyserver/certmagic/ocsp.go rename to vendor/github.com/coolaj86/certmagic/ocsp.go index 4163302..fedc70e 100644 --- a/vendor/github.com/caddyserver/certmagic/ocsp.go +++ b/vendor/github.com/coolaj86/certmagic/ocsp.go @@ -124,7 +124,7 @@ func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Res // IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return // values are nil, the OCSP status may be assumed OCSPUnknown. // -// Borrowed from github.com/go-acme/lego +// Borrowed from xenolf. func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) { // TODO: Perhaps this should be synchronized too, with a Locker? diff --git a/vendor/github.com/caddyserver/certmagic/ratelimiter.go b/vendor/github.com/coolaj86/certmagic/ratelimiter.go similarity index 87% rename from vendor/github.com/caddyserver/certmagic/ratelimiter.go rename to vendor/github.com/coolaj86/certmagic/ratelimiter.go index 0468045..6a3b7b1 100644 --- a/vendor/github.com/caddyserver/certmagic/ratelimiter.go +++ b/vendor/github.com/coolaj86/certmagic/ratelimiter.go @@ -1,7 +1,23 @@ +// Copyright 2015 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package certmagic import ( "context" + "log" + "runtime" "sync" "time" ) @@ -51,6 +67,14 @@ func (r *RingBufferRateLimiter) Stop() { } func (r *RingBufferRateLimiter) loop() { + defer func() { + if err := recover(); err != nil { + buf := make([]byte, stackTraceBufferSize) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("panic: ring buffer rate limiter: %v\n%s", err, buf) + } + }() + for { // if we've been stopped, return select { diff --git a/vendor/github.com/caddyserver/certmagic/solvers.go b/vendor/github.com/coolaj86/certmagic/solvers.go similarity index 63% rename from vendor/github.com/caddyserver/certmagic/solvers.go rename to vendor/github.com/coolaj86/certmagic/solvers.go index 50dad4d..c0957da 100644 --- a/vendor/github.com/caddyserver/certmagic/solvers.go +++ b/vendor/github.com/coolaj86/certmagic/solvers.go @@ -15,6 +15,7 @@ package certmagic import ( + "context" "crypto/tls" "encoding/json" "fmt" @@ -22,13 +23,15 @@ import ( "net" "net/http" "path" + "runtime" "strings" "sync" "sync/atomic" "time" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/challenge/tlsalpn01" + "github.com/libdns/libdns" + "github.com/mholt/acmez" + "github.com/mholt/acmez/acme" ) // httpSolver solves the HTTP challenge. It must be @@ -48,7 +51,7 @@ type httpSolver struct { } // Present starts an HTTP server if none is already listening on s.address. -func (s *httpSolver) Present(domain, token, keyAuth string) error { +func (s *httpSolver) Present(ctx context.Context, _ acme.Challenge) error { solversMu.Lock() defer solversMu.Unlock() @@ -75,6 +78,13 @@ func (s *httpSolver) Present(domain, token, keyAuth string) error { // serve is an HTTP server that serves only HTTP challenge responses. func (s *httpSolver) serve(si *solverInfo) { + defer func() { + if err := recover(); err != nil { + buf := make([]byte, stackTraceBufferSize) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("panic: http solver server: %v\n%s", err, buf) + } + }() defer close(si.done) httpServer := &http.Server{Handler: s.acmeManager.HTTPChallengeHandler(http.NewServeMux())} httpServer.SetKeepAlivesEnabled(false) @@ -85,7 +95,7 @@ func (s *httpSolver) serve(si *solverInfo) { } // CleanUp cleans up the HTTP server if it is the last one to finish. -func (s *httpSolver) CleanUp(domain, token, keyAuth string) error { +func (s *httpSolver) CleanUp(ctx context.Context, _ acme.Challenge) error { solversMu.Lock() defer solversMu.Unlock() si := getSolverInfo(s.address) @@ -112,20 +122,20 @@ type tlsALPNSolver struct { // Present adds the certificate to the certificate cache and, if // needed, starts a TLS server for answering TLS-ALPN challenges. -func (s *tlsALPNSolver) Present(domain, token, keyAuth string) error { +func (s *tlsALPNSolver) Present(ctx context.Context, chal acme.Challenge) error { // load the certificate into the cache; this isn't strictly necessary // if we're using the distributed solver since our GetCertificate // function will check storage for the keyAuth anyway, but it seems // like loading it into the cache is the right thing to do - cert, err := tlsalpn01.ChallengeCert(domain, keyAuth) + cert, err := acmez.TLSALPN01ChallengeCert(chal) if err != nil { return err } certHash := hashCertificateChain(cert.Certificate) s.config.certCache.mu.Lock() - s.config.certCache.cache[tlsALPNCertKeyName(domain)] = Certificate{ + s.config.certCache.cache[tlsALPNCertKeyName(chal.Identifier.Value)] = Certificate{ Certificate: *cert, - Names: []string{domain}, + Names: []string{chal.Identifier.Value}, hash: certHash, // perhaps not necesssary } s.config.certCache.mu.Unlock() @@ -159,6 +169,13 @@ func (s *tlsALPNSolver) Present(domain, token, keyAuth string) error { si.listener = tls.NewListener(ln, s.config.TLSConfig()) go func() { + defer func() { + if err := recover(); err != nil { + buf := make([]byte, stackTraceBufferSize) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("panic: tls-alpn solver server: %v\n%s", err, buf) + } + }() defer close(si.done) for { conn, err := si.listener.Accept() @@ -178,6 +195,13 @@ func (s *tlsALPNSolver) Present(domain, token, keyAuth string) error { // handleConn completes the TLS handshake and then closes conn. func (*tlsALPNSolver) handleConn(conn net.Conn) { + defer func() { + if err := recover(); err != nil { + buf := make([]byte, stackTraceBufferSize) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("panic: tls-alpn solver handler: %v\n%s", err, buf) + } + }() defer conn.Close() tlsConn, ok := conn.(*tls.Conn) if !ok { @@ -193,9 +217,9 @@ func (*tlsALPNSolver) handleConn(conn net.Conn) { // CleanUp removes the challenge certificate from the cache, and if // it is the last one to finish, stops the TLS server. -func (s *tlsALPNSolver) CleanUp(domain, token, keyAuth string) error { +func (s *tlsALPNSolver) CleanUp(ctx context.Context, chal acme.Challenge) error { s.config.certCache.mu.Lock() - delete(s.config.certCache.cache, tlsALPNCertKeyName(domain)) + delete(s.config.certCache.cache, tlsALPNCertKeyName(chal.Identifier.Value)) s.config.certCache.mu.Unlock() solversMu.Lock() @@ -223,6 +247,195 @@ func tlsALPNCertKeyName(sniName string) string { return sniName + ":acme-tls-alpn" } +// DNS01Solver is a type that makes libdns providers usable +// as ACME dns-01 challenge solvers. +// See https://github.com/libdns/libdns +type DNS01Solver struct { + // The implementation that interacts with the DNS + // provider to set or delete records. (REQUIRED) + DNSProvider ACMEDNSProvider + + // The TTL for the temporary challenge records. + TTL time.Duration + + // Maximum time to wait for temporary record to appear. + PropagationTimeout time.Duration + + // Preferred DNS resolver(s) to use when doing DNS lookups. + Resolvers []string + + txtRecords map[string]dnsPresentMemory // keyed by domain name + txtRecordsMu sync.Mutex +} + +// Present creates the DNS TXT record for the given ACME challenge. +func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) error { + dnsName := challenge.DNS01TXTRecordName() + keyAuth := challenge.DNS01KeyAuthorization() + + rec := libdns.Record{ + Type: "TXT", + Name: dnsName, + Value: keyAuth, + TTL: s.TTL, + } + + // multiple identifiers can have the same ACME challenge + // domain (e.g. example.com and *.example.com) so we need + // to ensure that we don't solve those concurrently and + // step on each challenges' metaphorical toes; see + // https://github.com/caddyserver/caddy/issues/3474 + activeDNSChallenges.Lock(dnsName) + + zone, err := findZoneByFQDN(dnsName, recursiveNameservers(s.Resolvers)) + if err != nil { + return fmt.Errorf("could not determine zone for domain %q: %v", dnsName, err) + } + + results, err := s.DNSProvider.AppendRecords(ctx, zone, []libdns.Record{rec}) + if err != nil { + return fmt.Errorf("adding temporary record for zone %s: %w", zone, err) + } + if len(results) != 1 { + return fmt.Errorf("expected one record, got %d: %v", len(results), results) + } + + // remember the record and zone we got so we can clean up more efficiently + s.txtRecordsMu.Lock() + if s.txtRecords == nil { + s.txtRecords = make(map[string]dnsPresentMemory) + } + s.txtRecords[dnsName] = dnsPresentMemory{dnsZone: zone, rec: results[0]} + s.txtRecordsMu.Unlock() + + return nil +} + +// Wait blocks until the TXT record created in Present() appears in +// authoritative lookups, i.e. until it has propagated, or until +// timeout, whichever is first. +func (s *DNS01Solver) Wait(ctx context.Context, challenge acme.Challenge) error { + dnsName := challenge.DNS01TXTRecordName() + keyAuth := challenge.DNS01KeyAuthorization() + + timeout := s.PropagationTimeout + if timeout == 0 { + timeout = 2 * time.Minute + } + const interval = 2 * time.Second + + resolvers := recursiveNameservers(s.Resolvers) + + var err error + start := time.Now() + for time.Since(start) < timeout { + select { + case <-time.After(interval): + case <-ctx.Done(): + return ctx.Err() + } + var ready bool + ready, err = checkDNSPropagation(dnsName, keyAuth, resolvers) + if err != nil { + return fmt.Errorf("checking DNS propagation of %s: %w", dnsName, err) + } + if ready { + return nil + } + } + + return fmt.Errorf("timed out waiting for record to fully propagate; verify DNS provider configuration is correct - last error: %v", err) +} + +// CleanUp deletes the DNS TXT record created in Present(). +func (s *DNS01Solver) CleanUp(ctx context.Context, challenge acme.Challenge) error { + dnsName := challenge.DNS01TXTRecordName() + + defer func() { + // always forget about it so we don't leak memory + s.txtRecordsMu.Lock() + delete(s.txtRecords, dnsName) + s.txtRecordsMu.Unlock() + + // always do this last - but always do it! + activeDNSChallenges.Unlock(dnsName) + }() + + // recall the record we created and zone we looked up + s.txtRecordsMu.Lock() + memory, ok := s.txtRecords[dnsName] + if !ok { + s.txtRecordsMu.Unlock() + return fmt.Errorf("no memory of presenting a DNS record for %s (probably OK if presenting failed)", challenge.Identifier.Value) + } + s.txtRecordsMu.Unlock() + + // clean up the record + _, err := s.DNSProvider.DeleteRecords(ctx, memory.dnsZone, []libdns.Record{memory.rec}) + if err != nil { + return fmt.Errorf("deleting temporary record for zone %s: %w", memory.dnsZone, err) + } + + return nil +} + +type dnsPresentMemory struct { + dnsZone string + rec libdns.Record +} + +// ACMEDNSProvider defines the set of operations required for +// ACME challenges. A DNS provider must be able to append and +// delete records in order to solve ACME challenges. Find one +// you can use at https://github.com/libdns. If your provider +// isn't implemented yet, feel free to contribute! +type ACMEDNSProvider interface { + libdns.RecordAppender + libdns.RecordDeleter +} + +// activeDNSChallenges synchronizes DNS challenges for +// names to ensure that challenges for the same ACME +// DNS name do not overlap; for example, the TXT record +// to make for both example.com and *.example.com are +// the same; thus we cannot solve them concurrently. +var activeDNSChallenges = newMapMutex() + +// mapMutex implements named mutexes. +type mapMutex struct { + cond *sync.Cond + set map[interface{}]struct{} +} + +func newMapMutex() *mapMutex { + return &mapMutex{ + cond: sync.NewCond(new(sync.Mutex)), + set: make(map[interface{}]struct{}), + } +} + +func (mmu *mapMutex) Lock(key interface{}) { + mmu.cond.L.Lock() + defer mmu.cond.L.Unlock() + for mmu.locked(key) { + mmu.cond.Wait() + } + mmu.set[key] = struct{}{} + return +} + +func (mmu *mapMutex) Unlock(key interface{}) { + mmu.cond.L.Lock() + defer mmu.cond.L.Unlock() + delete(mmu.set, key) + mmu.cond.Broadcast() +} + +func (mmu *mapMutex) locked(key interface{}) (ok bool) { + _, ok = mmu.set[key] + return +} + // distributedSolver allows the ACME HTTP-01 and TLS-ALPN challenges // to be solved by an instance other than the one which initiated it. // This is useful behind load balancers or in other cluster/fleet @@ -254,7 +467,7 @@ type distributedSolver struct { // Since the distributedSolver is only a // wrapper over an actual solver, place // the actual solver here. - providerServer challenge.Provider + solver acmez.Solver // The CA endpoint URL associated with // this solver. @@ -264,36 +477,32 @@ type distributedSolver struct { // Present invokes the underlying solver's Present method // and also stores domain, token, and keyAuth to the storage // backing the certificate cache of dhs.acmeManager. -func (dhs distributedSolver) Present(domain, token, keyAuth string) error { - infoBytes, err := json.Marshal(challengeInfo{ - Domain: domain, - Token: token, - KeyAuth: keyAuth, - }) +func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) error { + infoBytes, err := json.Marshal(chal) if err != nil { return err } - err = dhs.acmeManager.config.Storage.Store(dhs.challengeTokensKey(domain), infoBytes) + err = dhs.acmeManager.config.Storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes) if err != nil { return err } - err = dhs.providerServer.Present(domain, token, keyAuth) + err = dhs.solver.Present(ctx, chal) if err != nil { - return fmt.Errorf("presenting with embedded provider: %v", err) + return fmt.Errorf("presenting with embedded solver: %v", err) } return nil } // CleanUp invokes the underlying solver's CleanUp method // and also cleans up any assets saved to storage. -func (dhs distributedSolver) CleanUp(domain, token, keyAuth string) error { - err := dhs.acmeManager.config.Storage.Delete(dhs.challengeTokensKey(domain)) +func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) error { + err := dhs.acmeManager.config.Storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value)) if err != nil { return err } - err = dhs.providerServer.CleanUp(domain, token, keyAuth) + err = dhs.solver.CleanUp(ctx, chal) if err != nil { return fmt.Errorf("cleaning up embedded provider: %v", err) } @@ -311,10 +520,6 @@ func (dhs distributedSolver) challengeTokensKey(domain string) string { return path.Join(dhs.challengeTokensPrefix(), StorageKeys.Safe(domain)+".json") } -type challengeInfo struct { - Domain, Token, KeyAuth string -} - // solverInfo associates a listener with the // number of challenges currently using it. type solverInfo struct { @@ -410,3 +615,9 @@ var ( solvers = make(map[string]*solverInfo) solversMu sync.Mutex ) + +// Interface guards +var ( + _ acmez.Solver = (*DNS01Solver)(nil) + _ acmez.Waiter = (*DNS01Solver)(nil) +) diff --git a/vendor/github.com/caddyserver/certmagic/storage.go b/vendor/github.com/coolaj86/certmagic/storage.go similarity index 95% rename from vendor/github.com/caddyserver/certmagic/storage.go rename to vendor/github.com/coolaj86/certmagic/storage.go index 4b431bc..f5045a9 100644 --- a/vendor/github.com/caddyserver/certmagic/storage.go +++ b/vendor/github.com/coolaj86/certmagic/storage.go @@ -15,6 +15,7 @@ package certmagic import ( + "context" "log" "path" "regexp" @@ -86,8 +87,11 @@ type Locker interface { // To prevent deadlocks, all implementations (where this concern // is relevant) should put a reasonable expiration on the lock in // case Unlock is unable to be called due to some sort of network - // failure or system crash. - Lock(key string) error + // failure or system crash. Additionally, implementations should + // honor context cancellation as much as possible (in case the + // caller wishes to give up and free resources before the lock + // can be obtained). + Lock(ctx context.Context, key string) error // Unlock releases the lock for key. This method must ONLY be // called after a successful call to Lock, and only after the @@ -223,8 +227,8 @@ func CleanUpOwnLocks() { } } -func obtainLock(storage Storage, lockKey string) error { - err := storage.Lock(lockKey) +func acquireLock(ctx context.Context, storage Storage, lockKey string) error { + err := storage.Lock(ctx, lockKey) if err == nil { locksMu.Lock() locks[lockKey] = storage diff --git a/vendor/github.com/go-acme/lego/v3/certificate/authorization.go b/vendor/github.com/go-acme/lego/v3/certificate/authorization.go deleted file mode 100644 index f93413f..0000000 --- a/vendor/github.com/go-acme/lego/v3/certificate/authorization.go +++ /dev/null @@ -1,81 +0,0 @@ -package certificate - -import ( - "time" - - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/log" -) - -const ( - // overallRequestLimit is the overall number of request per second - // limited on the "new-reg", "new-authz" and "new-cert" endpoints. - // From the documentation the limitation is 20 requests per second, - // but using 20 as value doesn't work but 18 do - overallRequestLimit = 18 -) - -func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authorization, error) { - resc, errc := make(chan acme.Authorization), make(chan domainError) - - delay := time.Second / overallRequestLimit - - for _, authzURL := range order.Authorizations { - time.Sleep(delay) - - go func(authzURL string) { - authz, err := c.core.Authorizations.Get(authzURL) - if err != nil { - errc <- domainError{Domain: authz.Identifier.Value, Error: err} - return - } - - resc <- authz - }(authzURL) - } - - var responses []acme.Authorization - failures := make(obtainError) - for i := 0; i < len(order.Authorizations); i++ { - select { - case res := <-resc: - responses = append(responses, res) - case err := <-errc: - failures[err.Domain] = err.Error - } - } - - for i, auth := range order.Authorizations { - log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth) - } - - close(resc) - close(errc) - - // be careful to not return an empty failures map; - // even if empty, they become non-nil error values - if len(failures) > 0 { - return responses, failures - } - return responses, nil -} - -func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) { - for _, authzURL := range order.Authorizations { - auth, err := c.core.Authorizations.Get(authzURL) - if err != nil { - log.Infof("Unable to get the authorization for: %s", authzURL) - continue - } - - if auth.Status == acme.StatusValid { - log.Infof("Skipping deactivating of valid auth: %s", authzURL) - continue - } - - log.Infof("Deactivating auth: %s", authzURL) - if c.core.Authorizations.Deactivate(authzURL) != nil { - log.Infof("Unable to deactivate the authorization: %s", authzURL) - } - } -} diff --git a/vendor/github.com/go-acme/lego/v3/certificate/certificates.go b/vendor/github.com/go-acme/lego/v3/certificate/certificates.go deleted file mode 100644 index 43145bf..0000000 --- a/vendor/github.com/go-acme/lego/v3/certificate/certificates.go +++ /dev/null @@ -1,522 +0,0 @@ -package certificate - -import ( - "bytes" - "crypto" - "crypto/x509" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "net/http" - "strings" - "time" - - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/acme/api" - "github.com/go-acme/lego/v3/certcrypto" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/log" - "github.com/go-acme/lego/v3/platform/wait" - "golang.org/x/crypto/ocsp" - "golang.org/x/net/idna" -) - -// maxBodySize is the maximum size of body that we will read. -const maxBodySize = 1024 * 1024 - -// Resource represents a CA issued certificate. -// PrivateKey, Certificate and IssuerCertificate are all -// already PEM encoded and can be directly written to disk. -// Certificate may be a certificate bundle, -// depending on the options supplied to create it. -type Resource struct { - Domain string `json:"domain"` - CertURL string `json:"certUrl"` - CertStableURL string `json:"certStableUrl"` - PrivateKey []byte `json:"-"` - Certificate []byte `json:"-"` - IssuerCertificate []byte `json:"-"` - CSR []byte `json:"-"` -} - -// ObtainRequest The request to obtain certificate. -// -// The first domain in domains is used for the CommonName field of the certificate, -// all other domains are added using the Subject Alternate Names extension. -// -// A new private key is generated for every invocation of the function Obtain. -// If you do not want that you can supply your own private key in the privateKey parameter. -// If this parameter is non-nil it will be used instead of generating a new one. -// -// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. -type ObtainRequest struct { - Domains []string - Bundle bool - PrivateKey crypto.PrivateKey - MustStaple bool -} - -type resolver interface { - Solve(authorizations []acme.Authorization) error -} - -type CertifierOptions struct { - KeyType certcrypto.KeyType - Timeout time.Duration -} - -// Certifier A service to obtain/renew/revoke certificates. -type Certifier struct { - core *api.Core - resolver resolver - options CertifierOptions -} - -// NewCertifier creates a Certifier. -func NewCertifier(core *api.Core, resolver resolver, options CertifierOptions) *Certifier { - return &Certifier{ - core: core, - resolver: resolver, - options: options, - } -} - -// Obtain tries to obtain a single certificate using all domains passed into it. -// -// This function will never return a partial certificate. -// If one domain in the list fails, the whole certificate will fail. -func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) { - if len(request.Domains) == 0 { - return nil, errors.New("no domains to obtain a certificate for") - } - - domains := sanitizeDomain(request.Domains) - - if request.Bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) - } else { - log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) - } - - order, err := c.core.Orders.New(domains) - if err != nil { - return nil, err - } - - authz, err := c.getAuthorizations(order) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - err = c.resolver.Solve(authz) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - failures := make(obtainError) - cert, err := c.getForOrder(domains, order, request.Bundle, request.PrivateKey, request.MustStaple) - if err != nil { - for _, auth := range authz { - failures[challenge.GetTargetedDomain(auth)] = err - } - } - - // Do not return an empty failures map, because - // it would still be a non-nil error value - if len(failures) > 0 { - return cert, failures - } - return cert, nil -} - -// ObtainForCSR tries to obtain a certificate matching the CSR passed into it. -// -// The domains are inferred from the CommonName and SubjectAltNames, if any. -// The private key for this CSR is not required. -// -// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. -// -// This function will never return a partial certificate. -// If one domain in the list fails, the whole certificate will fail. -func (c *Certifier) ObtainForCSR(csr x509.CertificateRequest, bundle bool) (*Resource, error) { - // figure out what domains it concerns - // start with the common name - domains := certcrypto.ExtractDomainsCSR(&csr) - - if bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) - } else { - log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) - } - - order, err := c.core.Orders.New(domains) - if err != nil { - return nil, err - } - - authz, err := c.getAuthorizations(order) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - err = c.resolver.Solve(authz) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - failures := make(obtainError) - cert, err := c.getForCSR(domains, order, bundle, csr.Raw, nil) - if err != nil { - for _, auth := range authz { - failures[challenge.GetTargetedDomain(auth)] = err - } - } - - if cert != nil { - // Add the CSR to the certificate so that it can be used for renewals. - cert.CSR = certcrypto.PEMEncode(&csr) - } - - // Do not return an empty failures map, - // because it would still be a non-nil error value - if len(failures) > 0 { - return cert, failures - } - return cert, nil -} - -func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool) (*Resource, error) { - if privateKey == nil { - var err error - privateKey, err = certcrypto.GeneratePrivateKey(c.options.KeyType) - if err != nil { - return nil, err - } - } - - // Determine certificate name(s) based on the authorization resources - commonName := domains[0] - - // RFC8555 Section 7.4 "Applying for Certificate Issuance" - // https://tools.ietf.org/html/rfc8555#section-7.4 - // says: - // Clients SHOULD NOT make any assumptions about the sort order of - // "identifiers" or "authorizations" elements in the returned order - // object. - san := []string{commonName} - for _, auth := range order.Identifiers { - if auth.Value != commonName { - san = append(san, auth.Value) - } - } - - // TODO: should the CSR be customizable? - csr, err := certcrypto.GenerateCSR(privateKey, commonName, san, mustStaple) - if err != nil { - return nil, err - } - - return c.getForCSR(domains, order, bundle, csr, certcrypto.PEMEncode(privateKey)) -} - -func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle bool, csr []byte, privateKeyPem []byte) (*Resource, error) { - respOrder, err := c.core.Orders.UpdateForCSR(order.Finalize, csr) - if err != nil { - return nil, err - } - - commonName := domains[0] - certRes := &Resource{ - Domain: commonName, - CertURL: respOrder.Certificate, - PrivateKey: privateKeyPem, - } - - if respOrder.Status == acme.StatusValid { - // if the certificate is available right away, short cut! - ok, errR := c.checkResponse(respOrder, certRes, bundle) - if errR != nil { - return nil, errR - } - - if ok { - return certRes, nil - } - } - - timeout := c.options.Timeout - if c.options.Timeout <= 0 { - timeout = 30 * time.Second - } - - err = wait.For("certificate", timeout, timeout/60, func() (bool, error) { - ord, errW := c.core.Orders.Get(order.Location) - if errW != nil { - return false, errW - } - - done, errW := c.checkResponse(ord, certRes, bundle) - if errW != nil { - return false, errW - } - - return done, nil - }) - - return certRes, err -} - -// checkResponse checks to see if the certificate is ready and a link is contained in the response. -// -// If so, loads it into certRes and returns true. -// If the cert is not yet ready, it returns false. -// -// The certRes input should already have the Domain (common name) field populated. -// -// If bundle is true, the certificate will be bundled with the issuer's cert. -func (c *Certifier) checkResponse(order acme.Order, certRes *Resource, bundle bool) (bool, error) { - valid, err := checkOrderStatus(order) - if err != nil || !valid { - return valid, err - } - - cert, issuer, err := c.core.Certificates.Get(order.Certificate, bundle) - if err != nil { - return false, err - } - - log.Infof("[%s] Server responded with a certificate.", certRes.Domain) - - certRes.IssuerCertificate = issuer - certRes.Certificate = cert - certRes.CertURL = order.Certificate - certRes.CertStableURL = order.Certificate - - return true, nil -} - -// Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA. -func (c *Certifier) Revoke(cert []byte) error { - certificates, err := certcrypto.ParsePEMBundle(cert) - if err != nil { - return err - } - - x509Cert := certificates[0] - if x509Cert.IsCA { - return errors.New("certificate bundle starts with a CA certificate") - } - - revokeMsg := acme.RevokeCertMessage{ - Certificate: base64.RawURLEncoding.EncodeToString(x509Cert.Raw), - } - - return c.core.Certificates.Revoke(revokeMsg) -} - -// Renew takes a Resource and tries to renew the certificate. -// -// If the renewal process succeeds, the new certificate will ge returned in a new CertResource. -// Please be aware that this function will return a new certificate in ANY case that is not an error. -// If the server does not provide us with a new cert on a GET request to the CertURL -// this function will start a new-cert flow where a new certificate gets generated. -// -// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. -// -// For private key reuse the PrivateKey property of the passed in Resource should be non-nil. -func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool) (*Resource, error) { - // Input certificate is PEM encoded. - // Decode it here as we may need the decoded cert later on in the renewal process. - // The input may be a bundle or a single certificate. - certificates, err := certcrypto.ParsePEMBundle(certRes.Certificate) - if err != nil { - return nil, err - } - - x509Cert := certificates[0] - if x509Cert.IsCA { - return nil, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", certRes.Domain) - } - - // This is just meant to be informal for the user. - timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", certRes.Domain, int(timeLeft.Hours())) - - // We always need to request a new certificate to renew. - // Start by checking to see if the certificate was based off a CSR, - // and use that if it's defined. - if len(certRes.CSR) > 0 { - csr, errP := certcrypto.PemDecodeTox509CSR(certRes.CSR) - if errP != nil { - return nil, errP - } - - return c.ObtainForCSR(*csr, bundle) - } - - var privateKey crypto.PrivateKey - if certRes.PrivateKey != nil { - privateKey, err = certcrypto.ParsePEMPrivateKey(certRes.PrivateKey) - if err != nil { - return nil, err - } - } - - query := ObtainRequest{ - Domains: certcrypto.ExtractDomains(x509Cert), - Bundle: bundle, - PrivateKey: privateKey, - MustStaple: mustStaple, - } - return c.Obtain(query) -} - -// GetOCSP takes a PEM encoded cert or cert bundle returning the raw OCSP response, -// the parsed response, and an error, if any. -// -// The returned []byte can be passed directly into the OCSPStaple property of a tls.Certificate. -// If the bundle only contains the issued certificate, -// this function will try to get the issuer certificate from the IssuingCertificateURL in the certificate. -// -// If the []byte and/or ocsp.Response return values are nil, the OCSP status may be assumed OCSPUnknown. -func (c *Certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) { - certificates, err := certcrypto.ParsePEMBundle(bundle) - if err != nil { - return nil, nil, err - } - - // We expect the certificate slice to be ordered downwards the chain. - // SRV CRT -> CA. We need to pull the leaf and issuer certs out of it, - // which should always be the first two certificates. - // If there's no OCSP server listed in the leaf cert, there's nothing to do. - // And if we have only one certificate so far, we need to get the issuer cert. - - issuedCert := certificates[0] - - if len(issuedCert.OCSPServer) == 0 { - return nil, nil, errors.New("no OCSP server specified in cert") - } - - if len(certificates) == 1 { - // TODO: build fallback. If this fails, check the remaining array entries. - if len(issuedCert.IssuingCertificateURL) == 0 { - return nil, nil, errors.New("no issuing certificate URL") - } - - resp, errC := c.core.HTTPClient.Get(issuedCert.IssuingCertificateURL[0]) - if errC != nil { - return nil, nil, errC - } - defer resp.Body.Close() - - issuerBytes, errC := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) - if errC != nil { - return nil, nil, errC - } - - issuerCert, errC := x509.ParseCertificate(issuerBytes) - if errC != nil { - return nil, nil, errC - } - - // Insert it into the slice on position 0 - // We want it ordered right SRV CRT -> CA - certificates = append(certificates, issuerCert) - } - - issuerCert := certificates[1] - - // Finally kick off the OCSP request. - ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil) - if err != nil { - return nil, nil, err - } - - resp, err := c.core.HTTPClient.Post(issuedCert.OCSPServer[0], "application/ocsp-request", bytes.NewReader(ocspReq)) - if err != nil { - return nil, nil, err - } - defer resp.Body.Close() - - ocspResBytes, err := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) - if err != nil { - return nil, nil, err - } - - ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert) - if err != nil { - return nil, nil, err - } - - return ocspResBytes, ocspRes, nil -} - -// Get attempts to fetch the certificate at the supplied URL. -// The URL is the same as what would normally be supplied at the Resource's CertURL. -// -// The returned Resource will not have the PrivateKey and CSR fields populated as these will not be available. -// -// If bundle is true, the Certificate field in the returned Resource includes the issuer certificate. -func (c *Certifier) Get(url string, bundle bool) (*Resource, error) { - cert, issuer, err := c.core.Certificates.Get(url, bundle) - if err != nil { - return nil, err - } - - // Parse the returned cert bundle so that we can grab the domain from the common name. - x509Certs, err := certcrypto.ParsePEMBundle(cert) - if err != nil { - return nil, err - } - - return &Resource{ - Domain: x509Certs[0].Subject.CommonName, - Certificate: cert, - IssuerCertificate: issuer, - CertURL: url, - CertStableURL: url, - }, nil -} - -func checkOrderStatus(order acme.Order) (bool, error) { - switch order.Status { - case acme.StatusValid: - return true, nil - case acme.StatusInvalid: - return false, order.Error - default: - return false, nil - } -} - -// https://tools.ietf.org/html/rfc8555#section-7.1.4 -// The domain name MUST be encoded -// in the form in which it would appear in a certificate. That is, it -// MUST be encoded according to the rules in Section 7 of [RFC5280]. -// -// https://tools.ietf.org/html/rfc5280#section-7 -func sanitizeDomain(domains []string) []string { - var sanitizedDomains []string - for _, domain := range domains { - sanitizedDomain, err := idna.ToASCII(domain) - if err != nil { - log.Infof("skip domain %q: unable to sanitize (punnycode): %v", domain, err) - } else { - sanitizedDomains = append(sanitizedDomains, sanitizedDomain) - } - } - return sanitizedDomains -} diff --git a/vendor/github.com/go-acme/lego/v3/certificate/errors.go b/vendor/github.com/go-acme/lego/v3/certificate/errors.go deleted file mode 100644 index 3adbc78..0000000 --- a/vendor/github.com/go-acme/lego/v3/certificate/errors.go +++ /dev/null @@ -1,30 +0,0 @@ -package certificate - -import ( - "bytes" - "fmt" - "sort" -) - -// obtainError is returned when there are specific errors available per domain. -type obtainError map[string]error - -func (e obtainError) Error() string { - buffer := bytes.NewBufferString("error: one or more domains had a problem:\n") - - var domains []string - for domain := range e { - domains = append(domains, domain) - } - sort.Strings(domains) - - for _, domain := range domains { - buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain])) - } - return buffer.String() -} - -type domainError struct { - Domain string - Error error -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go b/vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go deleted file mode 100644 index 6ac6eab..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/http01/domain_matcher.go +++ /dev/null @@ -1,184 +0,0 @@ -package http01 - -import ( - "fmt" - "net/http" - "strings" -) - -// A domainMatcher tries to match a domain (the one we're requesting a certificate for) -// in the HTTP request coming from the ACME validation servers. -// This step is part of DNS rebind attack prevention, -// where the webserver matches incoming requests to a list of domain the server acts authoritative for. -// -// The most simple check involves finding the domain in the HTTP Host header; -// this is what hostMatcher does. -// Use it, when the http01.ProviderServer is directly reachable from the internet, -// or when it operates behind a transparent proxy. -// -// In many (reverse) proxy setups, Apache and NGINX traditionally move the Host header to a new header named X-Forwarded-Host. -// Use arbitraryMatcher("X-Forwarded-Host") in this case, -// or the appropriate header name for other proxy servers. -// -// RFC7239 has standardized the different forwarding headers into a single header named Forwarded. -// The header value has a different format, so you should use forwardedMatcher -// when the http01.ProviderServer operates behind a RFC7239 compatible proxy. -// https://tools.ietf.org/html/rfc7239 -// -// Note: RFC7239 also reminds us, "that an HTTP list [...] may be split over multiple header fields" (section 7.1), -// meaning that -// X-Header: a -// X-Header: b -// is equal to -// X-Header: a, b -// -// All matcher implementations (explicitly not excluding arbitraryMatcher!) -// have in common that they only match against the first value in such lists. -type domainMatcher interface { - // matches checks whether the request is valid for the given domain. - matches(request *http.Request, domain string) bool - - // name returns the header name used in the check. - // This is primarily used to create meaningful error messages. - name() string -} - -// hostMatcher checks whether (*net/http).Request.Host starts with a domain name. -type hostMatcher struct{} - -func (m *hostMatcher) name() string { - return "Host" -} - -func (m *hostMatcher) matches(r *http.Request, domain string) bool { - return strings.HasPrefix(r.Host, domain) -} - -// hostMatcher checks whether the specified (*net/http.Request).Header value starts with a domain name. -type arbitraryMatcher string - -func (m arbitraryMatcher) name() string { - return string(m) -} - -func (m arbitraryMatcher) matches(r *http.Request, domain string) bool { - return strings.HasPrefix(r.Header.Get(m.name()), domain) -} - -// forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name. -// See https://tools.ietf.org/html/rfc7239 for details. -type forwardedMatcher struct{} - -func (m *forwardedMatcher) name() string { - return "Forwarded" -} - -func (m *forwardedMatcher) matches(r *http.Request, domain string) bool { - fwds, err := parseForwardedHeader(r.Header.Get(m.name())) - if err != nil { - return false - } - - if len(fwds) == 0 { - return false - } - - host := fwds[0]["host"] - return strings.HasPrefix(host, domain) -} - -// parsing requires some form of state machine. -func parseForwardedHeader(s string) (elements []map[string]string, err error) { - cur := make(map[string]string) - key := "" - val := "" - inquote := false - - pos := 0 - l := len(s) - for i := 0; i < l; i++ { - r := rune(s[i]) - - if inquote { - if r == '"' { - cur[key] = s[pos:i] - key = "" - pos = i - inquote = false - } - continue - } - - switch { - case r == '"': // start of quoted-string - if key == "" { - return nil, fmt.Errorf("unexpected quoted string as pos %d", i) - } - inquote = true - pos = i + 1 - - case r == ';': // end of forwarded-pair - cur[key] = s[pos:i] - key = "" - i = skipWS(s, i) - pos = i + 1 - - case r == '=': // end of token - key = strings.ToLower(strings.TrimFunc(s[pos:i], isWS)) - i = skipWS(s, i) - pos = i + 1 - - case r == ',': // end of forwarded-element - if key != "" { - if val == "" { - val = s[pos:i] - } - cur[key] = val - } - elements = append(elements, cur) - cur = make(map[string]string) - key = "" - val = "" - - i = skipWS(s, i) - pos = i + 1 - case tchar(r) || isWS(r): // valid token character or whitespace - continue - default: - return nil, fmt.Errorf("invalid token character at pos %d: %c", i, r) - } - } - - if inquote { - return nil, fmt.Errorf("unterminated quoted-string at pos %d", len(s)) - } - - if key != "" { - if pos < len(s) { - val = s[pos:] - } - cur[key] = val - } - if len(cur) > 0 { - elements = append(elements, cur) - } - return elements, nil -} - -func tchar(r rune) bool { - return strings.ContainsRune("!#$%&'*+-.^_`|~", r) || - '0' <= r && r <= '9' || - 'a' <= r && r <= 'z' || - 'A' <= r && r <= 'Z' -} - -func skipWS(s string, i int) int { - for isWS(rune(s[i+1])) { - i++ - } - return i -} - -func isWS(r rune) bool { - return strings.ContainsRune(" \t\v\r\n", r) -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go b/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go deleted file mode 100644 index 7924b40..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge.go +++ /dev/null @@ -1,65 +0,0 @@ -package http01 - -import ( - "fmt" - - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/acme/api" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/log" -) - -type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error - -// ChallengePath returns the URL path for the `http-01` challenge. -func ChallengePath(token string) string { - return "/.well-known/acme-challenge/" + token -} - -type Challenge struct { - core *api.Core - validate ValidateFunc - provider challenge.Provider -} - -func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge { - return &Challenge{ - core: core, - validate: validate, - provider: provider, - } -} - -func (c *Challenge) SetProvider(provider challenge.Provider) { - c.provider = provider -} - -func (c *Challenge) Solve(authz acme.Authorization) error { - domain := challenge.GetTargetedDomain(authz) - log.Infof("[%s] acme: Trying to solve HTTP-01", domain) - - chlng, err := challenge.FindChallenge(challenge.HTTP01, authz) - if err != nil { - return err - } - - // Generate the Key Authorization for the challenge - keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) - if err != nil { - return err - } - - err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("[%s] acme: error presenting token: %w", domain, err) - } - defer func() { - err := c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth) - if err != nil { - log.Warnf("[%s] acme: cleaning up failed: %v", domain, err) - } - }() - - chlng.KeyAuthorization = keyAuth - return c.validate(c.core, domain, chlng) -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go b/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go deleted file mode 100644 index 7944217..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/http01/http_challenge_server.go +++ /dev/null @@ -1,122 +0,0 @@ -package http01 - -import ( - "fmt" - "net" - "net/http" - "net/textproto" - "strings" - - "github.com/go-acme/lego/v3/log" -) - -// ProviderServer implements ChallengeProvider for `http-01` challenge. -// It may be instantiated without using the NewProviderServer function if -// you want only to use the default values. -type ProviderServer struct { - iface string - port string - matcher domainMatcher - done chan bool - listener net.Listener -} - -// NewProviderServer creates a new ProviderServer on the selected interface and port. -// Setting iface and / or port to an empty string will make the server fall back to -// the "any" interface and port 80 respectively. -func NewProviderServer(iface, port string) *ProviderServer { - if port == "" { - port = "80" - } - - return &ProviderServer{iface: iface, port: port, matcher: &hostMatcher{}} -} - -// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests. -func (s *ProviderServer) Present(domain, token, keyAuth string) error { - var err error - s.listener, err = net.Listen("tcp", s.GetAddress()) - if err != nil { - return fmt.Errorf("could not start HTTP server for challenge: %w", err) - } - - s.done = make(chan bool) - go s.serve(domain, token, keyAuth) - return nil -} - -func (s *ProviderServer) GetAddress() string { - return net.JoinHostPort(s.iface, s.port) -} - -// CleanUp closes the HTTP server and removes the token from `ChallengePath(token)`. -func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { - if s.listener == nil { - return nil - } - s.listener.Close() - <-s.done - return nil -} - -// SetProxyHeader changes the validation of incoming requests. -// By default, s matches the "Host" header value to the domain name. -// -// When the server runs behind a proxy server, this is not the correct place to look at; -// Apache and NGINX have traditionally moved the original Host header into a new header named "X-Forwarded-Host". -// Other webservers might use different names; -// and RFC7239 has standardized a new header named "Forwarded" (with slightly different semantics). -// -// The exact behavior depends on the value of headerName: -// - "" (the empty string) and "Host" will restore the default and only check the Host header -// - "Forwarded" will look for a Forwarded header, and inspect it according to https://tools.ietf.org/html/rfc7239 -// - any other value will check the header value with the same name. -func (s *ProviderServer) SetProxyHeader(headerName string) { - switch h := textproto.CanonicalMIMEHeaderKey(headerName); h { - case "", "Host": - s.matcher = &hostMatcher{} - case "Forwarded": - s.matcher = &forwardedMatcher{} - default: - s.matcher = arbitraryMatcher(h) - } -} - -func (s *ProviderServer) serve(domain, token, keyAuth string) { - path := ChallengePath(token) - - // The incoming request must will be validated to prevent DNS rebind attacks. - // We only respond with the keyAuth, when we're receiving a GET requests with - // the "Host" header matching the domain (the latter is configurable though SetProxyHeader). - mux := http.NewServeMux() - mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && s.matcher.matches(r, domain) { - w.Header().Add("Content-Type", "text/plain") - _, err := w.Write([]byte(keyAuth)) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Infof("[%s] Served key authentication", domain) - } else { - log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the %s header properly.", r.Host, r.Method, s.matcher.name()) - _, err := w.Write([]byte("TEST")) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - } - }) - - httpServer := &http.Server{Handler: mux} - - // Once httpServer is shut down - // we don't want any lingering connections, so disable KeepAlives. - httpServer.SetKeepAlivesEnabled(false) - - err := httpServer.Serve(s.listener) - if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - log.Println(err) - } - s.done <- true -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/resolver/errors.go b/vendor/github.com/go-acme/lego/v3/challenge/resolver/errors.go deleted file mode 100644 index e9da1eb..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/resolver/errors.go +++ /dev/null @@ -1,25 +0,0 @@ -package resolver - -import ( - "bytes" - "fmt" - "sort" -) - -// obtainError is returned when there are specific errors available per domain. -type obtainError map[string]error - -func (e obtainError) Error() string { - buffer := bytes.NewBufferString("error: one or more domains had a problem:\n") - - var domains []string - for domain := range e { - domains = append(domains, domain) - } - sort.Strings(domains) - - for _, domain := range domains { - buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain])) - } - return buffer.String() -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/resolver/prober.go b/vendor/github.com/go-acme/lego/v3/challenge/resolver/prober.go deleted file mode 100644 index 60f3973..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/resolver/prober.go +++ /dev/null @@ -1,173 +0,0 @@ -package resolver - -import ( - "fmt" - "time" - - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/log" -) - -// Interface for all challenge solvers to implement. -type solver interface { - Solve(authorization acme.Authorization) error -} - -// Interface for challenges like dns, where we can set a record in advance for ALL challenges. -// This saves quite a bit of time vs creating the records and solving them serially. -type preSolver interface { - PreSolve(authorization acme.Authorization) error -} - -// Interface for challenges like dns, where we can solve all the challenges before to delete them. -type cleanup interface { - CleanUp(authorization acme.Authorization) error -} - -type sequential interface { - Sequential() (bool, time.Duration) -} - -// an authz with the solver we have chosen and the index of the challenge associated with it. -type selectedAuthSolver struct { - authz acme.Authorization - solver solver -} - -type Prober struct { - solverManager *SolverManager -} - -func NewProber(solverManager *SolverManager) *Prober { - return &Prober{ - solverManager: solverManager, - } -} - -// Solve Looks through the challenge combinations to find a solvable match. -// Then solves the challenges in series and returns. -func (p *Prober) Solve(authorizations []acme.Authorization) error { - failures := make(obtainError) - - var authSolvers []*selectedAuthSolver - var authSolversSequential []*selectedAuthSolver - - // Loop through the resources, basically through the domains. - // First pass just selects a solver for each authz. - for _, authz := range authorizations { - domain := challenge.GetTargetedDomain(authz) - if authz.Status == acme.StatusValid { - // Boulder might recycle recent validated authz (see issue #267) - log.Infof("[%s] acme: authorization already valid; skipping challenge", domain) - continue - } - - if solvr := p.solverManager.chooseSolver(authz); solvr != nil { - authSolver := &selectedAuthSolver{authz: authz, solver: solvr} - - switch s := solvr.(type) { - case sequential: - if ok, _ := s.Sequential(); ok { - authSolversSequential = append(authSolversSequential, authSolver) - } else { - authSolvers = append(authSolvers, authSolver) - } - default: - authSolvers = append(authSolvers, authSolver) - } - } else { - failures[domain] = fmt.Errorf("[%s] acme: could not determine solvers", domain) - } - } - - parallelSolve(authSolvers, failures) - - sequentialSolve(authSolversSequential, failures) - - // Be careful not to return an empty failures map, - // for even an empty obtainError is a non-nil error value - if len(failures) > 0 { - return failures - } - return nil -} - -func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) { - for i, authSolver := range authSolvers { - // Submit the challenge - domain := challenge.GetTargetedDomain(authSolver.authz) - - if solvr, ok := authSolver.solver.(preSolver); ok { - err := solvr.PreSolve(authSolver.authz) - if err != nil { - failures[domain] = err - cleanUp(authSolver.solver, authSolver.authz) - continue - } - } - - // Solve challenge - err := authSolver.solver.Solve(authSolver.authz) - if err != nil { - failures[domain] = err - cleanUp(authSolver.solver, authSolver.authz) - continue - } - - // Clean challenge - cleanUp(authSolver.solver, authSolver.authz) - - if len(authSolvers)-1 > i { - solvr := authSolver.solver.(sequential) - _, interval := solvr.Sequential() - log.Infof("sequence: wait for %s", interval) - time.Sleep(interval) - } - } -} - -func parallelSolve(authSolvers []*selectedAuthSolver, failures obtainError) { - // For all valid preSolvers, first submit the challenges so they have max time to propagate - for _, authSolver := range authSolvers { - authz := authSolver.authz - if solvr, ok := authSolver.solver.(preSolver); ok { - err := solvr.PreSolve(authz) - if err != nil { - failures[challenge.GetTargetedDomain(authz)] = err - } - } - } - - defer func() { - // Clean all created TXT records - for _, authSolver := range authSolvers { - cleanUp(authSolver.solver, authSolver.authz) - } - }() - - // Finally solve all challenges for real - for _, authSolver := range authSolvers { - authz := authSolver.authz - domain := challenge.GetTargetedDomain(authz) - if failures[domain] != nil { - // already failed in previous loop - continue - } - - err := authSolver.solver.Solve(authz) - if err != nil { - failures[domain] = err - } - } -} - -func cleanUp(solvr solver, authz acme.Authorization) { - if solvr, ok := solvr.(cleanup); ok { - domain := challenge.GetTargetedDomain(authz) - err := solvr.CleanUp(authz) - if err != nil { - log.Warnf("[%s] acme: cleaning up failed: %v ", domain, err) - } - } -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/resolver/solver_manager.go b/vendor/github.com/go-acme/lego/v3/challenge/resolver/solver_manager.go deleted file mode 100644 index bd79a4f..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/resolver/solver_manager.go +++ /dev/null @@ -1,169 +0,0 @@ -package resolver - -import ( - "context" - "errors" - "fmt" - "sort" - "strconv" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/acme/api" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/challenge/dns01" - "github.com/go-acme/lego/v3/challenge/http01" - "github.com/go-acme/lego/v3/challenge/tlsalpn01" - "github.com/go-acme/lego/v3/log" -) - -type byType []acme.Challenge - -func (a byType) Len() int { return len(a) } -func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byType) Less(i, j int) bool { return a[i].Type > a[j].Type } - -type SolverManager struct { - core *api.Core - solvers map[challenge.Type]solver -} - -func NewSolversManager(core *api.Core) *SolverManager { - return &SolverManager{ - solvers: map[challenge.Type]solver{}, - core: core, - } -} - -// SetHTTP01Provider specifies a custom provider p that can solve the given HTTP-01 challenge. -func (c *SolverManager) SetHTTP01Provider(p challenge.Provider) error { - c.solvers[challenge.HTTP01] = http01.NewChallenge(c.core, validate, p) - return nil -} - -// SetTLSALPN01Provider specifies a custom provider p that can solve the given TLS-ALPN-01 challenge. -func (c *SolverManager) SetTLSALPN01Provider(p challenge.Provider) error { - c.solvers[challenge.TLSALPN01] = tlsalpn01.NewChallenge(c.core, validate, p) - return nil -} - -// SetDNS01Provider specifies a custom provider p that can solve the given DNS-01 challenge. -func (c *SolverManager) SetDNS01Provider(p challenge.Provider, opts ...dns01.ChallengeOption) error { - c.solvers[challenge.DNS01] = dns01.NewChallenge(c.core, validate, p, opts...) - return nil -} - -// Remove Remove a challenge type from the available solvers. -func (c *SolverManager) Remove(chlgType challenge.Type) { - delete(c.solvers, chlgType) -} - -// Checks all challenges from the server in order and returns the first matching solver. -func (c *SolverManager) chooseSolver(authz acme.Authorization) solver { - // Allow to have a deterministic challenge order - sort.Sort(byType(authz.Challenges)) - - domain := challenge.GetTargetedDomain(authz) - for _, chlg := range authz.Challenges { - if solvr, ok := c.solvers[challenge.Type(chlg.Type)]; ok { - log.Infof("[%s] acme: use %s solver", domain, chlg.Type) - return solvr - } - log.Infof("[%s] acme: Could not find solver for: %s", domain, chlg.Type) - } - - return nil -} - -func validate(core *api.Core, domain string, chlg acme.Challenge) error { - chlng, err := core.Challenges.New(chlg.URL) - if err != nil { - return fmt.Errorf("failed to initiate challenge: %w", err) - } - - valid, err := checkChallengeStatus(chlng) - if err != nil { - return err - } - - if valid { - log.Infof("[%s] The server validated our request", domain) - return nil - } - - ra, err := strconv.Atoi(chlng.RetryAfter) - if err != nil { - // The ACME server MUST return a Retry-After. - // If it doesn't, we'll just poll hard. - // Boulder does not implement the ability to retry challenges or the Retry-After header. - // https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md#section-82 - ra = 5 - } - initialInterval := time.Duration(ra) * time.Second - - bo := backoff.NewExponentialBackOff() - bo.InitialInterval = initialInterval - bo.MaxInterval = 10 * initialInterval - bo.MaxElapsedTime = 100 * initialInterval - - ctx, cancel := context.WithCancel(context.Background()) - - // After the path is sent, the ACME server will access our server. - // Repeatedly check the server for an updated status on our request. - operation := func() error { - authz, err := core.Authorizations.Get(chlng.AuthorizationURL) - if err != nil { - cancel() - return err - } - - valid, err := checkAuthorizationStatus(authz) - if err != nil { - cancel() - return err - } - - if valid { - log.Infof("[%s] The server validated our request", domain) - return nil - } - - return errors.New("the server didn't respond to our request") - } - - return backoff.Retry(operation, backoff.WithContext(bo, ctx)) -} - -func checkChallengeStatus(chlng acme.ExtendedChallenge) (bool, error) { - switch chlng.Status { - case acme.StatusValid: - return true, nil - case acme.StatusPending, acme.StatusProcessing: - return false, nil - case acme.StatusInvalid: - return false, chlng.Error - default: - return false, errors.New("the server returned an unexpected state") - } -} - -func checkAuthorizationStatus(authz acme.Authorization) (bool, error) { - switch authz.Status { - case acme.StatusValid: - return true, nil - case acme.StatusPending, acme.StatusProcessing: - return false, nil - case acme.StatusDeactivated, acme.StatusExpired, acme.StatusRevoked: - return false, fmt.Errorf("the authorization state %s", authz.Status) - case acme.StatusInvalid: - for _, chlg := range authz.Challenges { - if chlg.Status == acme.StatusInvalid && chlg.Error != nil { - return false, chlg.Error - } - } - return false, fmt.Errorf("the authorization state %s", authz.Status) - default: - return false, errors.New("the server returned an unexpected state") - } -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge.go b/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge.go deleted file mode 100644 index 348e5e9..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge.go +++ /dev/null @@ -1,129 +0,0 @@ -package tlsalpn01 - -import ( - "crypto/rsa" - "crypto/sha256" - "crypto/tls" - "crypto/x509/pkix" - "encoding/asn1" - "fmt" - - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/acme/api" - "github.com/go-acme/lego/v3/certcrypto" - "github.com/go-acme/lego/v3/challenge" - "github.com/go-acme/lego/v3/log" -) - -// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension. -// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.1 -var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} - -type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error - -type Challenge struct { - core *api.Core - validate ValidateFunc - provider challenge.Provider -} - -func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge { - return &Challenge{ - core: core, - validate: validate, - provider: provider, - } -} - -func (c *Challenge) SetProvider(provider challenge.Provider) { - c.provider = provider -} - -// Solve manages the provider to validate and solve the challenge. -func (c *Challenge) Solve(authz acme.Authorization) error { - domain := authz.Identifier.Value - log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", challenge.GetTargetedDomain(authz)) - - chlng, err := challenge.FindChallenge(challenge.TLSALPN01, authz) - if err != nil { - return err - } - - // Generate the Key Authorization for the challenge - keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) - if err != nil { - return err - } - - err = c.provider.Present(domain, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("[%s] acme: error presenting token: %w", challenge.GetTargetedDomain(authz), err) - } - defer func() { - err := c.provider.CleanUp(domain, chlng.Token, keyAuth) - if err != nil { - log.Warnf("[%s] acme: cleaning up failed: %v", challenge.GetTargetedDomain(authz), err) - } - }() - - chlng.KeyAuthorization = keyAuth - return c.validate(c.core, domain, chlng) -} - -// ChallengeBlocks returns PEM blocks (certPEMBlock, keyPEMBlock) with the acmeValidation-v1 extension -// and domain name for the `tls-alpn-01` challenge. -func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) { - // Compute the SHA-256 digest of the key authorization. - zBytes := sha256.Sum256([]byte(keyAuth)) - - value, err := asn1.Marshal(zBytes[:sha256.Size]) - if err != nil { - return nil, nil, err - } - - // Add the keyAuth digest as the acmeValidation-v1 extension - // (marked as critical such that it won't be used by non-ACME software). - // Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-3 - extensions := []pkix.Extension{ - { - Id: idPeAcmeIdentifierV1, - Critical: true, - Value: value, - }, - } - - // Generate a new RSA key for the certificates. - tempPrivateKey, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) - if err != nil { - return nil, nil, err - } - - rsaPrivateKey := tempPrivateKey.(*rsa.PrivateKey) - - // Generate the PEM certificate using the provided private key, domain, and extra extensions. - tempCertPEM, err := certcrypto.GeneratePemCert(rsaPrivateKey, domain, extensions) - if err != nil { - return nil, nil, err - } - - // Encode the private key into a PEM format. We'll need to use it to generate the x509 keypair. - rsaPrivatePEM := certcrypto.PEMEncode(rsaPrivateKey) - - return tempCertPEM, rsaPrivatePEM, nil -} - -// ChallengeCert returns a certificate with the acmeValidation-v1 extension -// and domain name for the `tls-alpn-01` challenge. -func ChallengeCert(domain, keyAuth string) (*tls.Certificate, error) { - tempCertPEM, rsaPrivatePEM, err := ChallengeBlocks(domain, keyAuth) - if err != nil { - return nil, err - } - - cert, err := tls.X509KeyPair(tempCertPEM, rsaPrivatePEM) - if err != nil { - return nil, err - } - - return &cert, nil -} diff --git a/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge_server.go b/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge_server.go deleted file mode 100644 index 8d09585..0000000 --- a/vendor/github.com/go-acme/lego/v3/challenge/tlsalpn01/tls_alpn_challenge_server.go +++ /dev/null @@ -1,95 +0,0 @@ -package tlsalpn01 - -import ( - "crypto/tls" - "fmt" - "net" - "net/http" - "strings" - - "github.com/go-acme/lego/v3/log" -) - -const ( - // ACMETLS1Protocol is the ALPN Protocol ID for the ACME-TLS/1 Protocol. - ACMETLS1Protocol = "acme-tls/1" - - // defaultTLSPort is the port that the ProviderServer will default to - // when no other port is provided. - defaultTLSPort = "443" -) - -// ProviderServer implements ChallengeProvider for `TLS-ALPN-01` challenge. -// It may be instantiated without using the NewProviderServer -// if you want only to use the default values. -type ProviderServer struct { - iface string - port string - listener net.Listener -} - -// NewProviderServer creates a new ProviderServer on the selected interface and port. -// Setting iface and / or port to an empty string will make the server fall back to -// the "any" interface and port 443 respectively. -func NewProviderServer(iface, port string) *ProviderServer { - return &ProviderServer{iface: iface, port: port} -} - -func (s *ProviderServer) GetAddress() string { - return net.JoinHostPort(s.iface, s.port) -} - -// Present generates a certificate with a SHA-256 digest of the keyAuth provided -// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN spec. -func (s *ProviderServer) Present(domain, token, keyAuth string) error { - if s.port == "" { - // Fallback to port 443 if the port was not provided. - s.port = defaultTLSPort - } - - // Generate the challenge certificate using the provided keyAuth and domain. - cert, err := ChallengeCert(domain, keyAuth) - if err != nil { - return err - } - - // Place the generated certificate with the extension into the TLS config - // so that it can serve the correct details. - tlsConf := new(tls.Config) - tlsConf.Certificates = []tls.Certificate{*cert} - - // We must set that the `acme-tls/1` application level protocol is supported - // so that the protocol negotiation can succeed. Reference: - // https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.2 - tlsConf.NextProtos = []string{ACMETLS1Protocol} - - // Create the listener with the created tls.Config. - s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf) - if err != nil { - return fmt.Errorf("could not start HTTPS server for challenge: %w", err) - } - - // Shut the server down when we're finished. - go func() { - err := http.Serve(s.listener, nil) - if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - log.Println(err) - } - }() - - return nil -} - -// CleanUp closes the HTTPS server. -func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { - if s.listener == nil { - return nil - } - - // Server was created, close it. - if err := s.listener.Close(); err != nil && err != http.ErrServerClosed { - return err - } - - return nil -} diff --git a/vendor/github.com/go-acme/lego/v3/lego/client.go b/vendor/github.com/go-acme/lego/v3/lego/client.go deleted file mode 100644 index 2051d12..0000000 --- a/vendor/github.com/go-acme/lego/v3/lego/client.go +++ /dev/null @@ -1,74 +0,0 @@ -package lego - -import ( - "errors" - "net/url" - - "github.com/go-acme/lego/v3/acme/api" - "github.com/go-acme/lego/v3/certificate" - "github.com/go-acme/lego/v3/challenge/resolver" - "github.com/go-acme/lego/v3/registration" -) - -// Client is the user-friendly way to ACME. -type Client struct { - Certificate *certificate.Certifier - Challenge *resolver.SolverManager - Registration *registration.Registrar - core *api.Core -} - -// NewClient creates a new ACME client on behalf of the user. -// The client will depend on the ACME directory located at CADirURL for the rest of its actions. -// A private key of type keyType (see KeyType constants) will be generated when requesting a new certificate if one isn't provided. -func NewClient(config *Config) (*Client, error) { - if config == nil { - return nil, errors.New("a configuration must be provided") - } - - _, err := url.Parse(config.CADirURL) - if err != nil { - return nil, err - } - - if config.HTTPClient == nil { - return nil, errors.New("the HTTP client cannot be nil") - } - - privateKey := config.User.GetPrivateKey() - if privateKey == nil { - return nil, errors.New("private key was nil") - } - - var kid string - if reg := config.User.GetRegistration(); reg != nil { - kid = reg.URI - } - - core, err := api.New(config.HTTPClient, config.UserAgent, config.CADirURL, kid, privateKey) - if err != nil { - return nil, err - } - - solversManager := resolver.NewSolversManager(core) - - prober := resolver.NewProber(solversManager) - certifier := certificate.NewCertifier(core, prober, certificate.CertifierOptions{KeyType: config.Certificate.KeyType, Timeout: config.Certificate.Timeout}) - - return &Client{ - Certificate: certifier, - Challenge: solversManager, - Registration: registration.NewRegistrar(core, config.User), - core: core, - }, nil -} - -// GetToSURL returns the current ToS URL from the Directory. -func (c *Client) GetToSURL() string { - return c.core.GetDirectory().Meta.TermsOfService -} - -// GetExternalAccountRequired returns the External Account Binding requirement of the Directory. -func (c *Client) GetExternalAccountRequired() bool { - return c.core.GetDirectory().Meta.ExternalAccountRequired -} diff --git a/vendor/github.com/go-acme/lego/v3/lego/client_config.go b/vendor/github.com/go-acme/lego/v3/lego/client_config.go deleted file mode 100644 index 49e3a8b..0000000 --- a/vendor/github.com/go-acme/lego/v3/lego/client_config.go +++ /dev/null @@ -1,104 +0,0 @@ -package lego - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "net" - "net/http" - "os" - "time" - - "github.com/go-acme/lego/v3/certcrypto" - "github.com/go-acme/lego/v3/registration" -) - -const ( - // caCertificatesEnvVar is the environment variable name that can be used to - // specify the path to PEM encoded CA Certificates that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in - // the system-wide trusted root list. - caCertificatesEnvVar = "LEGO_CA_CERTIFICATES" - - // caServerNameEnvVar is the environment variable name that can be used to - // specify the CA server name that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in - // the system-wide trusted root list. - caServerNameEnvVar = "LEGO_CA_SERVER_NAME" - - // LEDirectoryProduction URL to the Let's Encrypt production - LEDirectoryProduction = "https://acme-v02.api.letsencrypt.org/directory" - - // LEDirectoryStaging URL to the Let's Encrypt staging - LEDirectoryStaging = "https://acme-staging-v02.api.letsencrypt.org/directory" -) - -type Config struct { - CADirURL string - User registration.User - UserAgent string - HTTPClient *http.Client - Certificate CertificateConfig -} - -func NewConfig(user registration.User) *Config { - return &Config{ - CADirURL: LEDirectoryProduction, - User: user, - HTTPClient: createDefaultHTTPClient(), - Certificate: CertificateConfig{ - KeyType: certcrypto.RSA2048, - Timeout: 30 * time.Second, - }, - } -} - -type CertificateConfig struct { - KeyType certcrypto.KeyType - Timeout time.Duration -} - -// createDefaultHTTPClient Creates an HTTP client with a reasonable timeout value -// and potentially a custom *x509.CertPool -// based on the caCertificatesEnvVar environment variable (see the `initCertPool` function). -func createDefaultHTTPClient() *http.Client { - return &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).DialContext, - TLSHandshakeTimeout: 15 * time.Second, - ResponseHeaderTimeout: 15 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - TLSClientConfig: &tls.Config{ - ServerName: os.Getenv(caServerNameEnvVar), - RootCAs: initCertPool(), - }, - }, - } -} - -// initCertPool creates a *x509.CertPool populated with the PEM certificates -// found in the filepath specified in the caCertificatesEnvVar OS environment -// variable. If the caCertificatesEnvVar is not set then initCertPool will -// return nil. If there is an error creating a *x509.CertPool from the provided -// caCertificatesEnvVar value then initCertPool will panic. -func initCertPool() *x509.CertPool { - if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" { - customCAs, err := ioutil.ReadFile(customCACertsPath) - if err != nil { - panic(fmt.Sprintf("error reading %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) - } - certPool := x509.NewCertPool() - if ok := certPool.AppendCertsFromPEM(customCAs); !ok { - panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) - } - return certPool - } - return nil -} diff --git a/vendor/github.com/go-acme/lego/v3/registration/registar.go b/vendor/github.com/go-acme/lego/v3/registration/registar.go deleted file mode 100644 index 77c390e..0000000 --- a/vendor/github.com/go-acme/lego/v3/registration/registar.go +++ /dev/null @@ -1,170 +0,0 @@ -package registration - -import ( - "errors" - "net/http" - - "github.com/go-acme/lego/v3/acme" - "github.com/go-acme/lego/v3/acme/api" - "github.com/go-acme/lego/v3/log" -) - -// Resource represents all important information about a registration -// of which the client needs to keep track itself. -// WARNING: will be remove in the future (acme.ExtendedAccount), https://github.com/go-acme/lego/issues/855. -type Resource struct { - Body acme.Account `json:"body,omitempty"` - URI string `json:"uri,omitempty"` -} - -type RegisterOptions struct { - TermsOfServiceAgreed bool -} - -type RegisterEABOptions struct { - TermsOfServiceAgreed bool - Kid string - HmacEncoded string -} - -type Registrar struct { - core *api.Core - user User -} - -func NewRegistrar(core *api.Core, user User) *Registrar { - return &Registrar{ - core: core, - user: user, - } -} - -// Register the current account to the ACME server. -func (r *Registrar) Register(options RegisterOptions) (*Resource, error) { - if r == nil || r.user == nil { - return nil, errors.New("acme: cannot register a nil client or user") - } - - accMsg := acme.Account{ - TermsOfServiceAgreed: options.TermsOfServiceAgreed, - Contact: []string{}, - } - - if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) - accMsg.Contact = []string{"mailto:" + r.user.GetEmail()} - } - - account, err := r.core.Accounts.New(accMsg) - if err != nil { - // FIXME seems impossible - errorDetails, ok := err.(acme.ProblemDetails) - if !ok || errorDetails.HTTPStatus != http.StatusConflict { - return nil, err - } - } - - return &Resource{URI: account.Location, Body: account.Account}, nil -} - -// RegisterWithExternalAccountBinding Register the current account to the ACME server. -func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOptions) (*Resource, error) { - accMsg := acme.Account{ - TermsOfServiceAgreed: options.TermsOfServiceAgreed, - Contact: []string{}, - } - - if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) - accMsg.Contact = []string{"mailto:" + r.user.GetEmail()} - } - - account, err := r.core.Accounts.NewEAB(accMsg, options.Kid, options.HmacEncoded) - if err != nil { - errorDetails, ok := err.(acme.ProblemDetails) - // FIXME seems impossible - if !ok || errorDetails.HTTPStatus != http.StatusConflict { - return nil, err - } - } - - return &Resource{URI: account.Location, Body: account.Account}, nil -} - -// QueryRegistration runs a POST request on the client's registration and returns the result. -// -// This is similar to the Register function, -// but acting on an existing registration link and resource. -func (r *Registrar) QueryRegistration() (*Resource, error) { - if r == nil || r.user == nil { - return nil, errors.New("acme: cannot query the registration of a nil client or user") - } - - // Log the URL here instead of the email as the email may not be set - log.Infof("acme: Querying account for %s", r.user.GetRegistration().URI) - - account, err := r.core.Accounts.Get(r.user.GetRegistration().URI) - if err != nil { - return nil, err - } - - return &Resource{ - Body: account, - // Location: header is not returned so this needs to be populated off of existing URI - URI: r.user.GetRegistration().URI, - }, nil -} - -// UpdateRegistration update the user registration on the ACME server. -func (r *Registrar) UpdateRegistration(options RegisterOptions) (*Resource, error) { - if r == nil || r.user == nil { - return nil, errors.New("acme: cannot update a nil client or user") - } - - accMsg := acme.Account{ - TermsOfServiceAgreed: options.TermsOfServiceAgreed, - Contact: []string{}, - } - - if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) - accMsg.Contact = []string{"mailto:" + r.user.GetEmail()} - } - - account, err := r.core.Accounts.Update(r.user.GetRegistration().URI, accMsg) - if err != nil { - return nil, err - } - - return &Resource{URI: account.Location, Body: account.Account}, nil -} - -// DeleteRegistration deletes the client's user registration from the ACME server. -func (r *Registrar) DeleteRegistration() error { - if r == nil || r.user == nil { - return errors.New("acme: cannot unregister a nil client or user") - } - - log.Infof("acme: Deleting account for %s", r.user.GetEmail()) - - return r.core.Accounts.Deactivate(r.user.GetRegistration().URI) -} - -// ResolveAccountByKey will attempt to look up an account using the given account key -// and return its registration resource. -func (r *Registrar) ResolveAccountByKey() (*Resource, error) { - log.Infof("acme: Trying to resolve account by key") - - accMsg := acme.Account{OnlyReturnExisting: true} - accountTransit, err := r.core.Accounts.New(accMsg) - if err != nil { - return nil, err - } - - account, err := r.core.Accounts.Get(accountTransit.Location) - if err != nil { - return nil, err - } - - return &Resource{URI: accountTransit.Location, Body: account}, nil -} diff --git a/vendor/github.com/go-acme/lego/v3/registration/user.go b/vendor/github.com/go-acme/lego/v3/registration/user.go deleted file mode 100644 index 1e29300..0000000 --- a/vendor/github.com/go-acme/lego/v3/registration/user.go +++ /dev/null @@ -1,13 +0,0 @@ -package registration - -import ( - "crypto" -) - -// User interface is to be implemented by users of this library. -// It is used by the client type to get user specific information. -type User interface { - GetEmail() string - GetRegistration() *Resource - GetPrivateKey() crypto.PrivateKey -} diff --git a/vendor/github.com/klauspost/cpuid/cpuid.go b/vendor/github.com/klauspost/cpuid/cpuid.go index 53cb48c..1db2bad 100644 --- a/vendor/github.com/klauspost/cpuid/cpuid.go +++ b/vendor/github.com/klauspost/cpuid/cpuid.go @@ -10,7 +10,13 @@ // Package home: https://github.com/klauspost/cpuid package cpuid -import "strings" +import ( + "math" + "strings" +) + +// AMD refererence: https://www.amd.com/system/files/TechDocs/25481.pdf +// and Processor Programming Reference (PPR) // Vendor is a representation of a CPU vendor. type Vendor int @@ -28,6 +34,8 @@ const ( XenHVM Bhyve Hygon + SiS + RDC ) const ( @@ -171,6 +179,7 @@ var flagNames = map[Flags]string{ type CPUInfo struct { BrandName string // Brand name reported by the CPU VendorID Vendor // Comparable CPU vendor ID + VendorString string // Raw vendor string. Features Flags // Features of the CPU PhysicalCores int // Number of physical processor cores in your CPU. Will be 0 if undetectable. ThreadsPerCore int // Number of threads per physical core. Will be 1 if undetectable. @@ -178,11 +187,12 @@ type CPUInfo struct { Family int // CPU family number Model int // CPU model number CacheLine int // Cache line size in bytes. Will be 0 if undetectable. + Hz int64 // Clock speed, if known Cache struct { L1I int // L1 Instruction Cache (per core or shared). Will be -1 if undetected L1D int // L1 Data Cache (per core or shared). Will be -1 if undetected L2 int // L2 Cache (per core or shared). Will be -1 if undetected - L3 int // L3 Instruction Cache (per core or shared). Will be -1 if undetected + L3 int // L3 Cache (per core, per ccx or shared). Will be -1 if undetected } SGX SGXSupport maxFunc uint32 @@ -224,7 +234,8 @@ func Detect() { CPU.ThreadsPerCore = threadsPerCore() CPU.LogicalCores = logicalCores() CPU.PhysicalCores = physicalCores() - CPU.VendorID = vendorID() + CPU.VendorID, CPU.VendorString = vendorID() + CPU.Hz = hertz(CPU.BrandName) CPU.cacheSize() } @@ -601,6 +612,65 @@ func (c CPUInfo) LogicalCPU() int { return int(ebx >> 24) } +// hertz tries to compute the clock speed of the CPU. If leaf 15 is +// supported, use it, otherwise parse the brand string. Yes, really. +func hertz(model string) int64 { + mfi := maxFunctionID() + if mfi >= 0x15 { + eax, ebx, ecx, _ := cpuid(0x15) + if eax != 0 && ebx != 0 && ecx != 0 { + return int64((int64(ecx) * int64(ebx)) / int64(eax)) + } + } + // computeHz determines the official rated speed of a CPU from its brand + // string. This insanity is *actually the official documented way to do + // this according to Intel*, prior to leaf 0x15 existing. The official + // documentation only shows this working for exactly `x.xx` or `xxxx` + // cases, e.g., `2.50GHz` or `1300MHz`; this parser will accept other + // sizes. + hz := strings.LastIndex(model, "Hz") + if hz < 3 { + return -1 + } + var multiplier int64 + switch model[hz-1] { + case 'M': + multiplier = 1000 * 1000 + case 'G': + multiplier = 1000 * 1000 * 1000 + case 'T': + multiplier = 1000 * 1000 * 1000 * 1000 + } + if multiplier == 0 { + return -1 + } + freq := int64(0) + divisor := int64(0) + decimalShift := int64(1) + var i int + for i = hz - 2; i >= 0 && model[i] != ' '; i-- { + if model[i] >= '0' && model[i] <= '9' { + freq += int64(model[i]-'0') * decimalShift + decimalShift *= 10 + } else if model[i] == '.' { + if divisor != 0 { + return -1 + } + divisor = decimalShift + } else { + return -1 + } + } + // we didn't find a space + if i < 0 { + return -1 + } + if divisor != 0 { + return (freq * multiplier) / divisor + } + return freq * multiplier +} + // VM Will return true if the cpu id indicates we are in // a virtual machine. This is only a hint, and will very likely // have many false negatives. @@ -659,11 +729,16 @@ func brandName() string { func threadsPerCore() int { mfi := maxFunctionID() - if mfi < 0x4 || vendorID() != Intel { + vend, _ := vendorID() + + if mfi < 0x4 || (vend != Intel && vend != AMD) { return 1 } if mfi < 0xb { + if vend != Intel { + return 1 + } _, b, _, d := cpuid(1) if (d & (1 << 28)) != 0 { // v will contain logical core count @@ -688,7 +763,8 @@ func threadsPerCore() int { func logicalCores() int { mfi := maxFunctionID() - switch vendorID() { + v, _ := vendorID() + switch v { case Intel: // Use this on old Intel processors if mfi < 0xb { @@ -723,10 +799,18 @@ func familyModel() (int, int) { } func physicalCores() int { - switch vendorID() { + v, _ := vendorID() + switch v { case Intel: return logicalCores() / threadsPerCore() case AMD, Hygon: + lc := logicalCores() + tpc := threadsPerCore() + if lc > 0 && tpc > 0 { + return lc / tpc + } + // The following is inaccurate on AMD EPYC 7742 64-Core Processor + if maxExtendedFunction() >= 0x80000008 { _, _, c, _ := cpuid(0x80000008) return int(c&0xff) + 1 @@ -751,16 +835,20 @@ var vendorMapping = map[string]Vendor{ "XenVMMXenVMM": XenHVM, "bhyve bhyve ": Bhyve, "HygonGenuine": Hygon, + "Vortex86 SoC": SiS, + "SiS SiS SiS ": SiS, + "RiseRiseRise": SiS, + "Genuine RDC": RDC, } -func vendorID() Vendor { +func vendorID() (Vendor, string) { _, b, c, d := cpuid(0) - v := valAsString(b, d, c) - vend, ok := vendorMapping[string(v)] + v := string(valAsString(b, d, c)) + vend, ok := vendorMapping[v] if !ok { - return Other + return Other, v } - return vend + return vend, v } func cacheLine() int { @@ -783,7 +871,7 @@ func (c *CPUInfo) cacheSize() { c.Cache.L1I = -1 c.Cache.L2 = -1 c.Cache.L3 = -1 - vendor := vendorID() + vendor, _ := vendorID() switch vendor { case Intel: if maxFunctionID() < 4 { @@ -837,6 +925,49 @@ func (c *CPUInfo) cacheSize() { } _, _, ecx, _ = cpuid(0x80000006) c.Cache.L2 = int(((ecx >> 16) & 0xFFFF) * 1024) + + // CPUID Fn8000_001D_EAX_x[N:0] Cache Properties + if maxExtendedFunction() < 0x8000001D { + return + } + for i := uint32(0); i < math.MaxUint32; i++ { + eax, ebx, ecx, _ := cpuidex(0x8000001D, i) + + level := (eax >> 5) & 7 + cacheNumSets := ecx + 1 + cacheLineSize := 1 + (ebx & 2047) + cachePhysPartitions := 1 + ((ebx >> 12) & 511) + cacheNumWays := 1 + ((ebx >> 22) & 511) + + typ := eax & 15 + size := int(cacheNumSets * cacheLineSize * cachePhysPartitions * cacheNumWays) + if typ == 0 { + return + } + + switch level { + case 1: + switch typ { + case 1: + // Data cache + c.Cache.L1D = size + case 2: + // Inst cache + c.Cache.L1I = size + default: + if c.Cache.L1D < 0 { + c.Cache.L1I = size + } + if c.Cache.L1I < 0 { + c.Cache.L1I = size + } + } + case 2: + c.Cache.L2 = size + case 3: + c.Cache.L3 = size + } + } } return @@ -895,7 +1026,7 @@ func hasSGX(available, lc bool) (rval SGXSupport) { func support() Flags { mfi := maxFunctionID() - vend := vendorID() + vend, _ := vendorID() if mfi < 0x1 { return 0 } @@ -954,7 +1085,11 @@ func support() Flags { rval |= HTT } } - + if vend == AMD && (d&(1<<28)) != 0 && mfi >= 4 { + if threadsPerCore() > 1 { + rval |= HTT + } + } // Check XGETBV, OXSAVE and AVX bits if c&(1<<26) != 0 && c&(1<<27) != 0 && c&(1<<28) != 0 { // Check for OS support @@ -1119,7 +1254,7 @@ func support() Flags { AV_CPU_FLAG_SSE2 and AV_CPU_FLAG_SSE2SLOW are both set in this case so that SSE2 is used unless explicitly disabled by checking AV_CPU_FLAG_SSE2SLOW. */ - if vendorID() != Intel && + if vend != Intel && rval&SSE2 != 0 && (c&0x00000040) == 0 { rval |= SSE2SLOW } @@ -1135,7 +1270,7 @@ func support() Flags { } } - if vendorID() == Intel { + if vend == Intel { family, model := familyModel() if family == 6 && (model == 9 || model == 13 || model == 14) { /* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and diff --git a/vendor/github.com/klauspost/cpuid/go.mod b/vendor/github.com/klauspost/cpuid/go.mod new file mode 100644 index 0000000..55563f2 --- /dev/null +++ b/vendor/github.com/klauspost/cpuid/go.mod @@ -0,0 +1,3 @@ +module github.com/klauspost/cpuid + +go 1.12 diff --git a/vendor/github.com/libdns/libdns/.gitignore b/vendor/github.com/libdns/libdns/.gitignore new file mode 100644 index 0000000..fbd281d --- /dev/null +++ b/vendor/github.com/libdns/libdns/.gitignore @@ -0,0 +1 @@ +_gitignore/ diff --git a/vendor/github.com/libdns/libdns/LICENSE b/vendor/github.com/libdns/libdns/LICENSE new file mode 100644 index 0000000..ac7653f --- /dev/null +++ b/vendor/github.com/libdns/libdns/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Matthew Holt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/libdns/libdns/README.md b/vendor/github.com/libdns/libdns/README.md new file mode 100644 index 0000000..e0ed429 --- /dev/null +++ b/vendor/github.com/libdns/libdns/README.md @@ -0,0 +1,48 @@ +libdns - Universal DNS provider APIs for Go +=========================================== + + + +`libdns` is a collection of free-range DNS provider client implementations written in Go! With libdns packages, your Go program can manage DNS records across any supported providers. + +**⚠️ Work-in-progress. Exported APIs are subject to change. More documentation is coming soon.** + +This repository defines the core interfaces that providers should implement. They are small and idiomatic Go interfaces with well-defined semantics. + +The interfaces include: + +- `RecordGetter` to list records. +- `RecordAppender` to append new records. +- `RecordSetter` to set (create or change existing) records. +- `RecordDeleter` to delete records. + + +## Implementing new providers + +Providers are 100% written and maintained by the community! We all maintain just the packages for providers we use. + +**[Instructions for adding new providers](https://github.com/libdns/libdns/wiki/Implementing-providers)** are on this repo's wiki. Please feel free to contribute. + + +## Similar projects + +**[OctoDNS](https://github.com/github/octodns)** is a suite of tools written in Python for managing DNS. However, its approach is a bit heavy-handed when all you need are small, incremental changes to a zone: + +> WARNING: OctoDNS assumes ownership of any domain you point it to. When you tell it to act it will do whatever is necessary to try and match up states including deleting any unexpected records. Be careful when playing around with OctoDNS. + +This is incredibly useful when you are maintaining your own zone file, but risky when you just need incremental changes. + +**[StackExchange/dnscontrol](https://github.com/StackExchange/dnscontrol)** is written in Go, but is similar to OctoDNS in that it tends to obliterate your entire zone and replace it with your input. Again, this is very useful if you are maintaining your own master list of records, but doesn't do well for simply adding or removing records. + +**[go-acme/lego](https://github.com/go-acme/lego)** has support for a huge number of DNS providers (75+!), but their APIs are only capable of setting and deleting TXT records for ACME challenges. + +**`libdns`** takes inspiration from the above projects but aims for a more generally-useful set of APIs that homogenize pretty well across providers. In contrast to the above projects, libdns can add, set, delete, and get arbitrary records from a zone without obliterating it (although syncing up an entire zone is also possible!). Its APIs also include context so long-running calls can be cancelled early, for example to accommodate on-line config changes downstream. libdns interfaces are also smaller and more composable. Additionally, libdns can grow to support a nearly infinite number of DNS providers without added bloat, because each provider implementation is a separate Go module, which keeps your builds lean and fast. + +In summary, the goal is that libdns providers can do what the above libraries/tools can do, but with more flexibility: they can create and delete TXT records for ACME challenges, they can replace entire zones, but they can also do incremental changes or simply read records. + + +## Record abstraction + +How records are represented across providers varies widely, and each kind of record has different fields and semantics. In time, our goal is for the `libdns.Record` type to be able to represent most of them as concisely and simply as possible, with the interface methods able to deliver on most of the possible zone operations. + +Realistically, libdns should enable most common record manipulations, but may not be able to fit absolutely 100% of all possibilities with DNS in a provider-agnostic way. That is probably OK; and given the wide varieties in DNS record types and provider APIs, it would be unreasonable to expect otherwise. We are not aiming for 100% fulfillment of 100% of users' requirements; more like 100% fulfillment of ~90% of users' requirements. diff --git a/vendor/github.com/libdns/libdns/go.mod b/vendor/github.com/libdns/libdns/go.mod new file mode 100644 index 0000000..2528495 --- /dev/null +++ b/vendor/github.com/libdns/libdns/go.mod @@ -0,0 +1,3 @@ +module github.com/libdns/libdns + +go 1.14 diff --git a/vendor/github.com/libdns/libdns/libdns.go b/vendor/github.com/libdns/libdns/libdns.go new file mode 100644 index 0000000..26920e6 --- /dev/null +++ b/vendor/github.com/libdns/libdns/libdns.go @@ -0,0 +1,85 @@ +// Package libdns defines the core interfaces that should be implemented +// by DNS provider clients. They are small and idiomatic Go interfaces with +// well-defined semantics. +// +// All interface implementations must be safe for concurrent/parallel use. +// For example, if AppendRecords() is called at the same time and two API +// requests are made to the provider at the same time, the result of both +// requests must be visible after they both complete; if the provider does +// not synchronize the writing of the zone file and one request overwrites +// the other, then the client implementation must take care to synchronize +// on behalf of the incompetent provider. This synchronization need not be +// global, for example: the scope of synchronization might only need to be +// within the same zone, allowing multiple requests at once as long as all +// of them are for different zones. (Exact logic depends on the provider.) +package libdns + +import ( + "context" + "time" +) + +// RecordGetter can get records from a DNS zone. +type RecordGetter interface { + // GetRecords returns all the records in the DNS zone. + // + // Implementations must honor context cancellation and be safe for + // concurrent use. + GetRecords(ctx context.Context, zone string) ([]Record, error) +} + +// RecordAppender can non-destructively add new records to a DNS zone. +type RecordAppender interface { + // AppendRecords creates the requested records in the given zone + // and returns the populated records that were created. It never + // changes existing records. + // + // Implementations must honor context cancellation and be safe for + // concurrent use. + AppendRecords(ctx context.Context, zone string, recs []Record) ([]Record, error) +} + +// RecordSetter can set new or update existing records in a DNS zone. +type RecordSetter interface { + // SetRecords updates the zone so that the records described in the + // input are reflected in the output. It may create or overwrite + // records or -- depending on the record type -- delete records to + // maintain parity with the input. No other records are affected. + // It returns the records which were set. + // + // Records that have an ID associating it with a particular resource + // on the provider will be directly replaced. If no ID is given, this + // method may use what information is given to do lookups and will + // ensure that only necessary changes are made to the zone. + // + // Implementations must honor context cancellation and be safe for + // concurrent use. + SetRecords(ctx context.Context, zone string, recs []Record) ([]Record, error) +} + +// RecordDeleter can delete records from a DNS zone. +type RecordDeleter interface { + // DeleteRecords deletes the given records from the zone if they exist. + // It returns the records that were deleted. + // + // Records that have an ID to associate it with a particular resource on + // the provider will be directly deleted. If no ID is given, this method + // may use what information is given to do lookups and delete only + // matching records. + // + // Implementations must honor context cancellation and be safe for + // concurrent use. + DeleteRecords(ctx context.Context, zone string, recs []Record) ([]Record, error) +} + +// Record is a generalized representation of a DNS record. +type Record struct { + // provider-specific metadata + ID string + + // general record fields + Type string + Name string + Value string + TTL time.Duration +} diff --git a/vendor/github.com/mholt/acmez/.gitignore b/vendor/github.com/mholt/acmez/.gitignore new file mode 100644 index 0000000..fbd281d --- /dev/null +++ b/vendor/github.com/mholt/acmez/.gitignore @@ -0,0 +1 @@ +_gitignore/ diff --git a/vendor/github.com/mholt/acmez/LICENSE b/vendor/github.com/mholt/acmez/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/vendor/github.com/mholt/acmez/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/mholt/acmez/README.md b/vendor/github.com/mholt/acmez/README.md new file mode 100644 index 0000000..c9c28aa --- /dev/null +++ b/vendor/github.com/mholt/acmez/README.md @@ -0,0 +1,59 @@ +acmez - ACME client library for Go +================================== + +[![godoc](https://pkg.go.dev/badge/github.com/mholt/acmez)](https://pkg.go.dev/github.com/mholt/acmez) + +ACMEz ("ack-measy" or "acme-zee", whichever you prefer) is a fully-compliant [RFC 8555](https://tools.ietf.org/html/rfc8555) (ACME) implementation in pure Go. It is lightweight, has an elegant Go API, and its retry logic is highly robust against external errors. ACMEz is suitable for large-scale enterprise deployments. + +**NOTE:** This module is for _getting_ certificates, not _managing_ certificates. Most users probably want certificate _management_ (keeping certificates renewed) rather than to interface directly with ACME. Developers who want to use certificates in their long-running Go programs should use [CertMagic](https://github.com/caddyserver/certmagic) instead; or, if their program is not written in Go, [Caddy](https://caddyserver.com/) can be used to manage certificates (even without running an HTTP or TLS server). + +This module has two primary packages: + +- **`acmez`** is a high-level wrapper for getting certificates. It implements the ACME order flow described in RFC 8555 including challenge solving using pluggable solvers. +- **`acme`** is a low-level RFC 8555 implementation that provides the fundamental ACME operations, mainly useful if you have advanced or niche requirements. + +In other words, the `acmez` package is **porcelain** while the `acme` package is **plumbing** (to use git's terminology). + + +## Features + +- Simple, elegant Go API +- Thoroughly documented with spec citations +- Robust to external errors +- Structured error values ("problems" as defined in RFC 7807) +- Smart retries (resilient against network and server hiccups) +- Challenge plasticity (randomized challenges, and will retry others if one fails) +- Context cancellation (suitable for high-frequency config changes or reloads) +- Highly flexible and customizable +- External Account Binding (EAB) support +- Tested with multiple ACME CAs (more than just Let's Encrypt) +- Supports niche aspects of RFC 8555 (such as alt cert chains and account key rollover) +- Efficient solving of large SAN lists (e.g. for slow DNS record propagation) +- Utility functions for solving challenges +- Helpers for RFC 8737 (tls-alpn-01 challenge) + +The `acmez` package is "bring-your-own-solver." It provides helper utilities for http-01, dns-01, and tls-alpn-01 challenges, but does not actually solve them for you. You must write an implementation of `acmez.Solver` in order to get certificates. How this is done depends on the environment in which you're using this code. + +This is not a command line utility either. The goal is to not add more external tooling to already-complex infrastructure: ACME and TLS should be built-in to servers rather than tacked on as an afterthought. + + +## Examples + +See the `examples` folder for tutorials on how to use either package. + + +## History + +In 2014, the ISRG was finishing the development of its automated CA infrastructure: the first of its kind to become publicly-trusted, under the name Let's Encrypt, which used a young protocol called ACME to automate domain validation and certificate issuance. + +Meanwhile, a project called [Caddy](https://caddyserver.com) was being developed which would be the first and only web server to use HTTPS _automatically and by default_. To make that possible, another project called lego was commissioned by the Caddy project to become of the first-ever ACME client libraries, and the first client written in Go. It was made by Sebastian Erhart (xenolf), and on day 1 of Let's Encrypt's public beta, Caddy used lego to obtain its first certificate automatically at startup, making Caddy and lego the first-ever integrated ACME client. + +Since then, Caddy has seen use in production longer than any other ACME client integration, and is well-known for being one of the most robust and reliable HTTPS implementations available today. + +A few years later, Caddy's novel auto-HTTPS logic was extracted into a library called [CertMagic](https://github.com/caddyserver/certmagic) to be usable by any Go program. Caddy would continue to use CertMagic, which implemented the certificate _automation and management_ logic on top of the low-level certificate _obtain_ logic that lego provided. + +Soon thereafter, the lego project shifted maintainership and the goals and vision of the project diverged from those of Caddy's use case of managing tens of thousands of certificates per instance. Eventually, [the original Caddy author announced work on a new ACME client library in Go](https://github.com/caddyserver/certmagic/issues/71) that exceeded Caddy's harsh requirements for large-scale enterprise deployments, lean builds, and simple API. This work finally came to fruition in 2020 as ACMEz. + +--- + +(c) 2020 Matthew Holt diff --git a/vendor/github.com/mholt/acmez/THIRD-PARTY b/vendor/github.com/mholt/acmez/THIRD-PARTY new file mode 100644 index 0000000..876c2ef --- /dev/null +++ b/vendor/github.com/mholt/acmez/THIRD-PARTY @@ -0,0 +1,37 @@ +This document contains Third Party Software Notices and/or Additional +Terms and Conditions for licensed third party software components +included within this product. + +== + +https://github.com/golang/crypto/blob/master/acme/jws.go +https://github.com/golang/crypto/blob/master/acme/jws_test.go +(with modifications) + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/mholt/acmez/acme/account.go b/vendor/github.com/mholt/acmez/acme/account.go new file mode 100644 index 0000000..b103eb2 --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/account.go @@ -0,0 +1,249 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acme + +import ( + "context" + "crypto" + "encoding/base64" + "encoding/json" + "fmt" +) + +// Account represents a set of metadata associated with an account +// as defined by the ACME spec §7.1.2: +// https://tools.ietf.org/html/rfc8555#section-7.1.2 +type Account struct { + // status (required, string): The status of this account. Possible + // values are "valid", "deactivated", and "revoked". The value + // "deactivated" should be used to indicate client-initiated + // deactivation whereas "revoked" should be used to indicate server- + // initiated deactivation. See Section 7.1.6. + Status string `json:"status"` + + // contact (optional, array of string): An array of URLs that the + // server can use to contact the client for issues related to this + // account. For example, the server may wish to notify the client + // about server-initiated revocation or certificate expiration. For + // information on supported URL schemes, see Section 7.3. + Contact []string `json:"contact,omitempty"` + + // termsOfServiceAgreed (optional, boolean): Including this field in a + // newAccount request, with a value of true, indicates the client's + // agreement with the terms of service. This field cannot be updated + // by the client. + TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"` + + // externalAccountBinding (optional, object): Including this field in a + // newAccount request indicates approval by the holder of an existing + // non-ACME account to bind that account to this ACME account. This + // field is not updateable by the client (see Section 7.3.4). + // + // Use SetExternalAccountBinding() to set this field's value properly. + ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"` + + // orders (required, string): A URL from which a list of orders + // submitted by this account can be fetched via a POST-as-GET + // request, as described in Section 7.1.2.1. + Orders string `json:"orders"` + + // In response to new-account, "the server returns this account + // object in a 201 (Created) response, with the account URL + // in a Location header field." §7.3 + // + // We transfer the value from the header to this field for + // storage and recall purposes. + Location string `json:"location,omitempty"` + + // The private key to the account. Because it is secret, it is + // not serialized as JSON and must be stored separately (usually + // a PEM-encoded file). + PrivateKey crypto.Signer `json:"-"` +} + +// SetExternalAccountBinding sets the ExternalAccountBinding field of the account. +// It only sets the field value; it does not register the account with the CA. (The +// client parameter is necessary because the EAB encoding depends on the directory.) +func (a *Account) SetExternalAccountBinding(ctx context.Context, client *Client, eab EAB) error { + if err := client.provision(ctx); err != nil { + return err + } + + macKey, err := base64.RawURLEncoding.DecodeString(eab.MACKey) + if err != nil { + return fmt.Errorf("base64-decoding MAC key: %w", err) + } + + eabJWS, err := jwsEncodeEAB(a.PrivateKey.Public(), macKey, keyID(eab.KeyID), client.dir.NewAccount) + if err != nil { + return fmt.Errorf("signing EAB content: %w", err) + } + + a.ExternalAccountBinding = eabJWS + + return nil +} + +// NewAccount creates a new account on the ACME server. +// +// "A client creates a new account with the server by sending a POST +// request to the server's newAccount URL." §7.3 +func (c *Client) NewAccount(ctx context.Context, account Account) (Account, error) { + if err := c.provision(ctx); err != nil { + return account, err + } + return c.postAccount(ctx, c.dir.NewAccount, accountObject{Account: account}) +} + +// GetAccount looks up an account on the ACME server. +// +// "If a client wishes to find the URL for an existing account and does +// not want an account to be created if one does not already exist, then +// it SHOULD do so by sending a POST request to the newAccount URL with +// a JWS whose payload has an 'onlyReturnExisting' field set to 'true'." +// §7.3.1 +func (c *Client) GetAccount(ctx context.Context, account Account) (Account, error) { + if err := c.provision(ctx); err != nil { + return account, err + } + return c.postAccount(ctx, c.dir.NewAccount, accountObject{ + Account: account, + OnlyReturnExisting: true, + }) +} + +// UpdateAccount updates account information on the ACME server. +// +// "If the client wishes to update this information in the future, it +// sends a POST request with updated information to the account URL. +// The server MUST ignore any updates to the 'orders' field, +// 'termsOfServiceAgreed' field (see Section 7.3.3), the 'status' field +// (except as allowed by Section 7.3.6), or any other fields it does not +// recognize." §7.3.2 +// +// This method uses the account.Location value as the account URL. +func (c *Client) UpdateAccount(ctx context.Context, account Account) (Account, error) { + return c.postAccount(ctx, account.Location, accountObject{Account: account}) +} + +type keyChangeRequest struct { + Account string `json:"account"` + OldKey json.RawMessage `json:"oldKey"` +} + +// AccountKeyRollover changes an account's associated key. +// +// "To change the key associated with an account, the client sends a +// request to the server containing signatures by both the old and new +// keys." §7.3.5 +func (c *Client) AccountKeyRollover(ctx context.Context, account Account, newPrivateKey crypto.Signer) (Account, error) { + if err := c.provision(ctx); err != nil { + return account, err + } + + oldPublicKeyJWK, err := jwkEncode(account.PrivateKey.Public()) + if err != nil { + return account, fmt.Errorf("encoding old private key: %v", err) + } + + keyChangeReq := keyChangeRequest{ + Account: account.Location, + OldKey: []byte(oldPublicKeyJWK), + } + + innerJWS, err := jwsEncodeJSON(keyChangeReq, newPrivateKey, "", "", c.dir.KeyChange) + if err != nil { + return account, fmt.Errorf("encoding inner JWS: %v", err) + } + + _, err = c.httpPostJWS(ctx, account.PrivateKey, account.Location, c.dir.KeyChange, json.RawMessage(innerJWS), nil) + if err != nil { + return account, fmt.Errorf("rolling key on server: %w", err) + } + + account.PrivateKey = newPrivateKey + + return account, nil + +} + +func (c *Client) postAccount(ctx context.Context, endpoint string, account accountObject) (Account, error) { + // Normally, the account URL is the key ID ("kid")... except when the user + // is trying to get the correct account URL. In that case, we must ignore + // any existing URL we may have and not set the kid field on the request. + // Arguably, this is a user error (spec says "If client wishes to find the + // URL for an existing account", so why would the URL already be filled + // out?) but it's easy enough to infer their intent and make it work. + kid := account.Location + if account.OnlyReturnExisting { + kid = "" + } + + resp, err := c.httpPostJWS(ctx, account.PrivateKey, kid, endpoint, account, &account.Account) + if err != nil { + return account.Account, err + } + + account.Location = resp.Header.Get("Location") + + return account.Account, nil +} + +type accountObject struct { + Account + + // If true, newAccount will be read-only, and Account.Location + // (which holds the account URL) must be empty. + OnlyReturnExisting bool `json:"onlyReturnExisting,omitempty"` +} + +// EAB (External Account Binding) contains information +// necessary to bind or map an ACME account to some +// other account known by the CA. +// +// External account bindings are "used to associate an +// ACME account with an existing account in a non-ACME +// system, such as a CA customer database." +// +// "To enable ACME account binding, the CA operating the +// ACME server needs to provide the ACME client with a +// MAC key and a key identifier, using some mechanism +// outside of ACME." §7.3.4 +type EAB struct { + // "The key identifier MUST be an ASCII string." §7.3.4 + KeyID string `json:"key_id"` + + // "The MAC key SHOULD be provided in base64url-encoded + // form, to maximize compatibility between non-ACME + // provisioning systems and ACME clients." §7.3.4 + MACKey string `json:"mac_key"` +} + +// Possible status values. From several spec sections: +// - Account §7.1.2 (valid, deactivated, revoked) +// - Order §7.1.3 (pending, ready, processing, valid, invalid) +// - Authorization §7.1.4 (pending, valid, invalid, deactivated, expired, revoked) +// - Challenge §7.1.5 (pending, processing, valid, invalid) +// - Status changes §7.1.6 +const ( + StatusPending = "pending" + StatusProcessing = "processing" + StatusValid = "valid" + StatusInvalid = "invalid" + StatusDeactivated = "deactivated" + StatusExpired = "expired" + StatusRevoked = "revoked" + StatusReady = "ready" +) diff --git a/vendor/github.com/mholt/acmez/acme/authorization.go b/vendor/github.com/mholt/acmez/acme/authorization.go new file mode 100644 index 0000000..3e69dcc --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/authorization.go @@ -0,0 +1,283 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acme + +import ( + "context" + "fmt" + "time" +) + +// Authorization "represents a server's authorization for +// an account to represent an identifier. In addition to the +// identifier, an authorization includes several metadata fields, such +// as the status of the authorization (e.g., 'pending', 'valid', or +// 'revoked') and which challenges were used to validate possession of +// the identifier." §7.1.4 +type Authorization struct { + // identifier (required, object): The identifier that the account is + // authorized to represent. + Identifier Identifier `json:"identifier"` + + // status (required, string): The status of this authorization. + // Possible values are "pending", "valid", "invalid", "deactivated", + // "expired", and "revoked". See Section 7.1.6. + Status string `json:"status"` + + // expires (optional, string): The timestamp after which the server + // will consider this authorization invalid, encoded in the format + // specified in [RFC3339]. This field is REQUIRED for objects with + // "valid" in the "status" field. + Expires time.Time `json:"expires,omitempty"` + + // challenges (required, array of objects): For pending authorizations, + // the challenges that the client can fulfill in order to prove + // possession of the identifier. For valid authorizations, the + // challenge that was validated. For invalid authorizations, the + // challenge that was attempted and failed. Each array entry is an + // object with parameters required to validate the challenge. A + // client should attempt to fulfill one of these challenges, and a + // server should consider any one of the challenges sufficient to + // make the authorization valid. + Challenges []Challenge `json:"challenges"` + + // wildcard (optional, boolean): This field MUST be present and true + // for authorizations created as a result of a newOrder request + // containing a DNS identifier with a value that was a wildcard + // domain name. For other authorizations, it MUST be absent. + // Wildcard domain names are described in Section 7.1.3. + Wildcard bool `json:"wildcard,omitempty"` + + // "The server allocates a new URL for this authorization and returns a + // 201 (Created) response with the authorization URL in the Location + // header field" §7.4.1 + // + // We transfer the value from the header to this field for storage and + // recall purposes. + Location string `json:"-"` +} + +// IdentifierValue returns the Identifier.Value field, adjusted +// according to the Wildcard field. +func (authz Authorization) IdentifierValue() string { + if authz.Wildcard { + return "*." + authz.Identifier.Value + } + return authz.Identifier.Value +} + +// fillChallengeFields populates extra fields in the challenge structs so that +// challenges can be solved without needing a bunch of unnecessary extra state. +func (authz *Authorization) fillChallengeFields(account Account) error { + accountThumbprint, err := jwkThumbprint(account.PrivateKey.Public()) + if err != nil { + return fmt.Errorf("computing account JWK thumbprint: %v", err) + } + for i := 0; i < len(authz.Challenges); i++ { + authz.Challenges[i].Identifier = authz.Identifier + if authz.Challenges[i].KeyAuthorization == "" { + authz.Challenges[i].KeyAuthorization = authz.Challenges[i].Token + "." + accountThumbprint + } + } + return nil +} + +// NewAuthorization creates a new authorization for an identifier using +// the newAuthz endpoint of the directory, if available. This function +// creates authzs out of the regular order flow. +// +// "Note that because the identifier in a pre-authorization request is +// the exact identifier to be included in the authorization object, pre- +// authorization cannot be used to authorize issuance of certificates +// containing wildcard domain names." §7.4.1 +func (c *Client) NewAuthorization(ctx context.Context, account Account, id Identifier) (Authorization, error) { + if err := c.provision(ctx); err != nil { + return Authorization{}, err + } + if c.dir.NewAuthz == "" { + return Authorization{}, fmt.Errorf("server does not support newAuthz endpoint") + } + + var authz Authorization + resp, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, c.dir.NewAuthz, id, &authz) + if err != nil { + return authz, err + } + + authz.Location = resp.Header.Get("Location") + + err = authz.fillChallengeFields(account) + if err != nil { + return authz, err + } + + return authz, nil +} + +// GetAuthorization fetches an authorization object from the server. +// +// "Authorization resources are created by the server in response to +// newOrder or newAuthz requests submitted by an account key holder; +// their URLs are provided to the client in the responses to these +// requests." +// +// "When a client receives an order from the server in reply to a +// newOrder request, it downloads the authorization resources by sending +// POST-as-GET requests to the indicated URLs. If the client initiates +// authorization using a request to the newAuthz resource, it will have +// already received the pending authorization object in the response to +// that request." §7.5 +func (c *Client) GetAuthorization(ctx context.Context, account Account, authzURL string) (Authorization, error) { + if err := c.provision(ctx); err != nil { + return Authorization{}, err + } + + var authz Authorization + _, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, authzURL, nil, &authz) + if err != nil { + return authz, err + } + + authz.Location = authzURL + + err = authz.fillChallengeFields(account) + if err != nil { + return authz, err + } + + return authz, nil +} + +// PollAuthorization polls the authorization resource endpoint until the authorization is +// considered "finalized" which means that it either succeeded, failed, or was abandoned. +// It blocks until that happens or until the configured timeout. +// +// "Usually, the validation process will take some time, so the client +// will need to poll the authorization resource to see when it is +// finalized." +// +// "For challenges where the client can tell when the server +// has validated the challenge (e.g., by seeing an HTTP or DNS request +// from the server), the client SHOULD NOT begin polling until it has +// seen the validation request from the server." §7.5.1 +func (c *Client) PollAuthorization(ctx context.Context, account Account, authz Authorization) (Authorization, error) { + start, interval, maxDuration := time.Now(), c.pollInterval(), c.pollTimeout() + + if authz.Status != "" { + if finalized, err := authzIsFinalized(authz); finalized { + return authz, err + } + } + + for time.Since(start) < maxDuration { + select { + case <-time.After(interval): + case <-ctx.Done(): + return authz, ctx.Err() + } + + // get the latest authz object + resp, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, authz.Location, nil, &authz) + if err != nil { + return authz, fmt.Errorf("checking authorization status: %w", err) + } + if finalized, err := authzIsFinalized(authz); finalized { + return authz, err + } + + // "The server MUST provide information about its retry state to the + // client via the 'error' field in the challenge and the Retry-After + // HTTP header field in response to requests to the challenge resource." + // §8.2 + interval, err = retryAfter(resp, interval) + if err != nil { + return authz, err + } + } + + return authz, fmt.Errorf("authorization took too long") +} + +// DeactivateAuthorization deactivates an authorization on the server, which is +// a good idea if the authorization is not going to be utilized by the client. +// +// "If a client wishes to relinquish its authorization to issue +// certificates for an identifier, then it may request that the server +// deactivate each authorization associated with it by sending POST +// requests with the static object {"status": "deactivated"} to each +// authorization URL." §7.5.2 +func (c *Client) DeactivateAuthorization(ctx context.Context, account Account, authzURL string) (Authorization, error) { + if err := c.provision(ctx); err != nil { + return Authorization{}, err + } + + if authzURL == "" { + return Authorization{}, fmt.Errorf("empty authz url") + } + + deactivate := struct { + Status string `json:"status"` + }{Status: "deactivated"} + + var authz Authorization + _, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, authzURL, deactivate, &authz) + authz.Location = authzURL + + return authz, err +} + +// authzIsFinalized returns true if the authorization is finished, +// whether successfully or not. If not, an error will be returned. +// Post-valid statuses that make an authz unusable are treated as +// errors. +func authzIsFinalized(authz Authorization) (bool, error) { + switch authz.Status { + case StatusPending: + // "Authorization objects are created in the 'pending' state." §7.1.6 + return false, nil + + case StatusValid: + // "If one of the challenges listed in the authorization transitions + // to the 'valid' state, then the authorization also changes to the + // 'valid' state." §7.1.6 + return true, nil + + case StatusInvalid: + // "If the client attempts to fulfill a challenge and fails, or if + // there is an error while the authorization is still pending, then + // the authorization transitions to the 'invalid' state." §7.1.6 + var firstProblem Problem + for _, chal := range authz.Challenges { + if chal.Error != nil { + firstProblem = *chal.Error + break + } + } + firstProblem.Resource = authz + return true, fmt.Errorf("authorization failed: %w", firstProblem) + + case StatusExpired, StatusDeactivated, StatusRevoked: + // Once the authorization is in the 'valid' state, it can expire + // ('expired'), be deactivated by the client ('deactivated', see + // Section 7.5.2), or revoked by the server ('revoked')." §7.1.6 + return true, fmt.Errorf("authorization %s", authz.Status) + + case "": + return false, fmt.Errorf("status unknown") + + default: + return true, fmt.Errorf("server set unrecognized authorization status: %s", authz.Status) + } +} diff --git a/vendor/github.com/mholt/acmez/acme/certificate.go b/vendor/github.com/mholt/acmez/acme/certificate.go new file mode 100644 index 0000000..a778280 --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/certificate.go @@ -0,0 +1,165 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acme + +import ( + "bytes" + "context" + "crypto" + "crypto/x509" + "encoding/base64" + "fmt" + "net/http" +) + +// Certificate represents a certificate chain, which we usually refer +// to as "a certificate" because in practice an end-entity certificate +// is seldom useful/practical without a chain. +type Certificate struct { + // The certificate resource URL as provisioned by + // the ACME server. Some ACME servers may split + // the chain into multiple URLs that are Linked + // together, in which case this URL represents the + // starting point. + URL string `json:"url"` + + // The PEM-encoded certificate chain, end-entity first. + ChainPEM []byte `json:"-"` +} + +// GetCertificateChain downloads all available certificate chains originating from +// the given certURL. This is to be done after an order is finalized. +// +// "To download the issued certificate, the client simply sends a POST- +// as-GET request to the certificate URL." +// +// "The server MAY provide one or more link relation header fields +// [RFC8288] with relation 'alternate'. Each such field SHOULD express +// an alternative certificate chain starting with the same end-entity +// certificate. This can be used to express paths to various trust +// anchors. Clients can fetch these alternates and use their own +// heuristics to decide which is optimal." §7.4.2 +func (c *Client) GetCertificateChain(ctx context.Context, account Account, certURL string) ([]Certificate, error) { + if err := c.provision(ctx); err != nil { + return nil, err + } + + var chains []Certificate + + addChain := func(certURL string) (*http.Response, error) { + // can't pool this buffer; bytes escape scope + buf := new(bytes.Buffer) + + // TODO: set the Accept header? ("application/pem-certificate-chain") See end of §7.4.2 + resp, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, certURL, nil, buf) + if err != nil { + return resp, err + } + contentType := parseMediaType(resp) + + switch contentType { + case "application/pem-certificate-chain": + chains = append(chains, Certificate{ + URL: certURL, + ChainPEM: buf.Bytes(), + }) + default: + return resp, fmt.Errorf("unrecognized Content-Type from server: %s", contentType) + } + + // "For formats that can only express a single certificate, the server SHOULD + // provide one or more "Link: rel="up"" header fields pointing to an + // issuer or issuers so that ACME clients can build a certificate chain + // as defined in TLS (see Section 4.4.2 of [RFC8446])." (end of §7.4.2) + allUp := extractLinks(resp, "up") + for _, upURL := range allUp { + upCerts, err := c.GetCertificateChain(ctx, account, upURL) + if err != nil { + return resp, fmt.Errorf("retrieving next certificate in chain: %s: %w", upURL, err) + } + for _, upCert := range upCerts { + chains[len(chains)-1].ChainPEM = append(chains[len(chains)-1].ChainPEM, upCert.ChainPEM...) + } + } + + return resp, nil + } + + // always add preferred/first certificate chain + resp, err := addChain(certURL) + if err != nil { + return chains, err + } + + // "The server MAY provide one or more link relation header fields + // [RFC8288] with relation 'alternate'. Each such field SHOULD express + // an alternative certificate chain starting with the same end-entity + // certificate. This can be used to express paths to various trust + // anchors. Clients can fetch these alternates and use their own + // heuristics to decide which is optimal." §7.4.2 + alternates := extractLinks(resp, "alternate") + for _, altURL := range alternates { + resp, err = addChain(altURL) + if err != nil { + return nil, fmt.Errorf("retrieving alternate certificate chain at %s: %w", altURL, err) + } + } + + return chains, nil +} + +// RevokeCertificate revokes the given certificate. If the certificate key is not +// provided, then the account key is used instead. See §7.6. +func (c *Client) RevokeCertificate(ctx context.Context, account Account, cert *x509.Certificate, certKey crypto.Signer, reason int) error { + if err := c.provision(ctx); err != nil { + return err + } + + body := struct { + Certificate string `json:"certificate"` + Reason int `json:"reason"` + }{ + Certificate: base64.RawURLEncoding.EncodeToString(cert.Raw), + Reason: reason, + } + + // "Revocation requests are different from other ACME requests in that + // they can be signed with either an account key pair or the key pair in + // the certificate." §7.6 + kid := "" + if certKey == account.PrivateKey { + kid = account.Location + } + + _, err := c.httpPostJWS(ctx, certKey, kid, c.dir.RevokeCert, body, nil) + return err +} + +// Reasons for revoking a certificate, as defined +// by RFC 5280 §5.3.1. +// https://tools.ietf.org/html/rfc5280#section-5.3.1 +const ( + ReasonUnspecified = iota // 0 + ReasonKeyCompromise // 1 + ReasonCACompromise // 2 + ReasonAffiliationChanged // 3 + ReasonSuperseded // 4 + ReasonCessationOfOperation // 5 + ReasonCertificateHold // 6 + _ // 7 (unused) + ReasonRemoveFromCRL // 8 + ReasonPrivilegeWithdrawn // 9 + ReasonAACompromise // 10 +) diff --git a/vendor/github.com/mholt/acmez/acme/challenge.go b/vendor/github.com/mholt/acmez/acme/challenge.go new file mode 100644 index 0000000..ccb264c --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/challenge.go @@ -0,0 +1,133 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acme + +import ( + "context" + "crypto/sha256" + "encoding/base64" +) + +// Challenge holds information about an ACME challenge. +// +// "An ACME challenge object represents a server's offer to validate a +// client's possession of an identifier in a specific way. Unlike the +// other objects listed above, there is not a single standard structure +// for a challenge object. The contents of a challenge object depend on +// the validation method being used. The general structure of challenge +// objects and an initial set of validation methods are described in +// Section 8." §7.1.5 +type Challenge struct { + // "Challenge objects all contain the following basic fields..." §8 + + // type (required, string): The type of challenge encoded in the + // object. + Type string `json:"type"` + + // url (required, string): The URL to which a response can be posted. + URL string `json:"url"` + + // status (required, string): The status of this challenge. Possible + // values are "pending", "processing", "valid", and "invalid" (see + // Section 7.1.6). + Status string `json:"status"` + + // validated (optional, string): The time at which the server validated + // this challenge, encoded in the format specified in [RFC3339]. + // This field is REQUIRED if the "status" field is "valid". + Validated string `json:"validated,omitempty"` + + // error (optional, object): Error that occurred while the server was + // validating the challenge, if any, structured as a problem document + // [RFC7807]. Multiple errors can be indicated by using subproblems + // Section 6.7.1. A challenge object with an error MUST have status + // equal to "invalid". + Error *Problem `json:"error,omitempty"` + + // "All additional fields are specified by the challenge type." §8 + // (We also add our own for convenience.) + + // "The token for a challenge is a string comprised entirely of + // characters in the URL-safe base64 alphabet." §8.1 + // + // Used by the http-01, tls-alpn-01, and dns-01 challenges. + Token string `json:"token,omitempty"` + + // A key authorization is a string that concatenates the token for the + // challenge with a key fingerprint, separated by a "." character (§8.1): + // + // keyAuthorization = token || '.' || base64url(Thumbprint(accountKey)) + // + // This client package automatically assembles and sets this value for you. + KeyAuthorization string `json:"keyAuthorization,omitempty"` + + // We attach the identifier that this challenge is associated with, which + // may be useful information for solving a challenge. It is not part of the + // structure as defined by the spec but is added by us to provide enough + // information to solve the DNS-01 challenge. + Identifier Identifier `json:"identifier,omitempty"` +} + +// HTTP01ResourcePath returns the URI path for solving the http-01 challenge. +// +// "The path at which the resource is provisioned is comprised of the +// fixed prefix '/.well-known/acme-challenge/', followed by the 'token' +// value in the challenge." §8.3 +func (c Challenge) HTTP01ResourcePath() string { + return "/.well-known/acme-challenge/" + c.Token +} + +// DNS01TXTRecordName returns the name of the TXT record to create for +// solving the dns-01 challenge. +// +// "The client constructs the validation domain name by prepending the +// label '_acme-challenge' to the domain name being validated, then +// provisions a TXT record with the digest value under that name." §8.4 +func (c Challenge) DNS01TXTRecordName() string { + return "_acme-challenge." + c.Identifier.Value +} + +// DNS01KeyAuthorization encodes a key authorization value to be used +// in a TXT record for the _acme-challenge DNS record. +// +// "A client fulfills this challenge by constructing a key authorization +// from the 'token' value provided in the challenge and the client's +// account key. The client then computes the SHA-256 digest [FIPS180-4] +// of the key authorization. +// +// The record provisioned to the DNS contains the base64url encoding of +// this digest." §8.4 +func (c Challenge) DNS01KeyAuthorization() string { + h := sha256.Sum256([]byte(c.KeyAuthorization)) + return base64.RawURLEncoding.EncodeToString(h[:]) +} + +// InitiateChallenge "indicates to the server that it is ready for the challenge +// validation by sending an empty JSON body ('{}') carried in a POST request to +// the challenge URL (not the authorization URL)." §7.5.1 +func (c *Client) InitiateChallenge(ctx context.Context, account Account, challenge Challenge) (Challenge, error) { + if err := c.provision(ctx); err != nil { + return Challenge{}, err + } + _, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, challenge.URL, struct{}{}, &challenge) + return challenge, err +} + +// The standard or well-known ACME challenge types. +const ( + ChallengeTypeHTTP01 = "http-01" // RFC 8555 §8.3 + ChallengeTypeDNS01 = "dns-01" // RFC 8555 §8.4 + ChallengeTypeTLSALPN01 = "tls-alpn-01" // RFC 8737 §3 +) diff --git a/vendor/github.com/mholt/acmez/acme/client.go b/vendor/github.com/mholt/acmez/acme/client.go new file mode 100644 index 0000000..5037905 --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/client.go @@ -0,0 +1,240 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package acme full implements the ACME protocol specification as +// described in RFC 8555: https://tools.ietf.org/html/rfc8555. +// +// It is designed to work smoothly in large-scale deployments with +// high resilience to errors and intermittent network or server issues, +// with retries built-in at every layer of the HTTP request stack. +// +// NOTE: This is a low-level API. Most users will want the mholt/acmez +// package which is more concerned with configuring challenges and +// implementing the order flow. However, using this package directly +// is recommended for advanced use cases having niche requirements. +// See the examples in the examples/plumbing folder for a tutorial. +package acme + +import ( + "context" + "fmt" + "net/http" + "sync" + "time" + + "go.uber.org/zap" +) + +// Client facilitates ACME client operations as defined by the spec. +// +// Because the client is synchronized for concurrent use, it should +// not be copied. +// +// Many errors that are returned by a Client are likely to be of type +// Problem as long as the ACME server returns a structured error +// response. This package wraps errors that may be of type Problem, +// so you can access the details using the conventional Go pattern: +// +// var problem Problem +// if errors.As(err, &problem) { +// log.Printf("Houston, we have a problem: %+v", problem) +// } +// +// All Problem errors originate from the ACME server. +type Client struct { + // The ACME server's directory endpoint. + Directory string + + // Custom HTTP client. + HTTPClient *http.Client + + // Augmentation of the User-Agent header. Please set + // this so that CAs can troubleshoot bugs more easily. + UserAgent string + + // Delay between poll attempts. Only used if server + // does not supply a Retry-Afer header. Default: 250ms + PollInterval time.Duration + + // Maximum duration for polling. Default: 5m + PollTimeout time.Duration + + // An optional logger. Default: no logs + Logger *zap.Logger + + mu sync.Mutex // protects all unexported fields + dir Directory + nonces *stack +} + +// GetDirectory retrieves the directory configured at c.Directory. It is +// NOT necessary to call this to provision the client. It is only useful +// if you want to access a copy of the directory yourself. +func (c *Client) GetDirectory(ctx context.Context) (Directory, error) { + if err := c.provision(ctx); err != nil { + return Directory{}, err + } + return c.dir, nil +} + +func (c *Client) provision(ctx context.Context) error { + c.mu.Lock() + defer c.mu.Unlock() + + if c.nonces == nil { + c.nonces = new(stack) + } + + err := c.provisionDirectory(ctx) + if err != nil { + return fmt.Errorf("provisioning client: %w", err) + } + + return nil +} + +func (c *Client) provisionDirectory(ctx context.Context) error { + // don't get directory again if we already have it; + // checking any one of the required fields will do + if c.dir.NewNonce != "" { + return nil + } + if c.Directory == "" { + return fmt.Errorf("missing directory URL") + } + // prefer cached version if it's recent enough + directoriesMu.Lock() + defer directoriesMu.Unlock() + if dir, ok := directories[c.Directory]; ok { + if time.Since(dir.retrieved) < 12*time.Hour { + c.dir = dir.Directory + return nil + } + } + _, err := c.httpReq(ctx, http.MethodGet, c.Directory, nil, &c.dir) + if err != nil { + return err + } + directories[c.Directory] = cachedDirectory{c.dir, time.Now()} + return nil +} + +func (c *Client) nonce(ctx context.Context) (string, error) { + nonce := c.nonces.pop() + if nonce != "" { + return nonce, nil + } + + if c.dir.NewNonce == "" { + return "", fmt.Errorf("directory missing newNonce endpoint") + } + + resp, err := c.httpReq(ctx, http.MethodHead, c.dir.NewNonce, nil, nil) + if err != nil { + return "", fmt.Errorf("fetching new nonce from server: %w", err) + } + + return resp.Header.Get(replayNonce), nil +} + +func (c *Client) pollInterval() time.Duration { + if c.PollInterval == 0 { + return defaultPollInterval + } + return c.PollInterval +} + +func (c *Client) pollTimeout() time.Duration { + if c.PollTimeout == 0 { + return defaultPollTimeout + } + return c.PollTimeout +} + +// Directory acts as an index for the ACME server as +// specified in the spec: "In order to help clients +// configure themselves with the right URLs for each +// ACME operation, ACME servers provide a directory +// object." §7.1.1 +type Directory struct { + NewNonce string `json:"newNonce"` + NewAccount string `json:"newAccount"` + NewOrder string `json:"newOrder"` + NewAuthz string `json:"newAuthz,omitempty"` + RevokeCert string `json:"revokeCert"` + KeyChange string `json:"keyChange"` + Meta *DirectoryMeta `json:"meta,omitempty"` +} + +// DirectoryMeta is optional extra data that may be +// included in an ACME server directory. §7.1.1 +type DirectoryMeta struct { + TermsOfService string `json:"termsOfService,omitempty"` + Website string `json:"website,omitempty"` + CAAIdentities []string `json:"caaIdentities,omitempty"` + ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"` +} + +// stack is a simple thread-safe stack. +type stack struct { + stack []string + stackMu sync.Mutex +} + +func (s *stack) push(v string) { + if v == "" { + return + } + s.stackMu.Lock() + defer s.stackMu.Unlock() + if len(s.stack) >= 64 { + return + } + s.stack = append(s.stack, v) +} + +func (s *stack) pop() string { + s.stackMu.Lock() + defer s.stackMu.Unlock() + n := len(s.stack) + if n == 0 { + return "" + } + v := s.stack[n-1] + s.stack = s.stack[:n-1] + return v +} + +// Directories seldom (if ever) change in practice, and +// client structs are often ephemeral, so we can cache +// directories to speed things up a bit for the user. +// Keyed by directory URL. +var ( + directories = make(map[string]cachedDirectory) + directoriesMu sync.Mutex +) + +type cachedDirectory struct { + Directory + retrieved time.Time +} + +// replayNonce is the header field that contains a new +// anti-replay nonce from the server. +const replayNonce = "Replay-Nonce" + +const ( + defaultPollInterval = 250 * time.Millisecond + defaultPollTimeout = 5 * time.Minute +) diff --git a/vendor/github.com/mholt/acmez/acme/http.go b/vendor/github.com/mholt/acmez/acme/http.go new file mode 100644 index 0000000..8312757 --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/http.go @@ -0,0 +1,394 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acme + +import ( + "bytes" + "context" + "crypto" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "time" + + "go.uber.org/zap" +) + +// httpPostJWS performs robust HTTP requests by JWS-encoding the JSON of input. +// If output is specified, the response body is written into it: if the response +// Content-Type is JSON, it will be JSON-decoded into output (which must be a +// pointer); otherwise, if output is an io.Writer, the response body will be +// written to it uninterpreted. In all cases, the returned response value's +// body will have been drained and closed, so there is no need to close it again. +// It automatically retries in the case of network, I/O, or badNonce errors. +func (c *Client) httpPostJWS(ctx context.Context, privateKey crypto.Signer, + kid, endpoint string, input, output interface{}) (*http.Response, error) { + + if err := c.provision(ctx); err != nil { + return nil, err + } + + var resp *http.Response + var err error + + // we can retry on internal server errors just in case it was a hiccup, + // but we probably don't need to retry so many times in that case + internalServerErrors, maxInternalServerErrors := 0, 3 + + // set a hard cap on the number of retries for any other reason + const maxAttempts = 10 + var attempts int + for attempts = 1; attempts <= maxAttempts; attempts++ { + if attempts > 1 { + select { + case <-time.After(250 * time.Millisecond): + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + var nonce string // avoid shadowing err + nonce, err = c.nonce(ctx) + if err != nil { + return nil, err + } + + var encodedPayload []byte // avoid shadowing err + encodedPayload, err = jwsEncodeJSON(input, privateKey, keyID(kid), nonce, endpoint) + if err != nil { + return nil, fmt.Errorf("encoding payload: %v", err) + } + + resp, err = c.httpReq(ctx, http.MethodPost, endpoint, encodedPayload, output) + if err == nil { + return resp, nil + } + + // "When a server rejects a request because its nonce value was + // unacceptable (or not present), it MUST provide HTTP status code 400 + // (Bad Request), and indicate the ACME error type + // 'urn:ietf:params:acme:error:badNonce'. An error response with the + // 'badNonce' error type MUST include a Replay-Nonce header field with a + // fresh nonce that the server will accept in a retry of the original + // query (and possibly in other requests, according to the server's + // nonce scoping policy). On receiving such a response, a client SHOULD + // retry the request using the new nonce." §6.5 + var problem Problem + if errors.As(err, &problem) { + if problem.Type == ProblemTypeBadNonce { + if c.Logger != nil { + c.Logger.Debug("server rejected our nonce; retrying", + zap.String("detail", problem.Detail), + zap.Error(err)) + } + continue + } + } + + // internal server errors *could* just be a hiccup and it may be worth + // trying again, but not nearly so many times as for other reasons + if resp != nil && resp.StatusCode >= 500 { + internalServerErrors++ + if internalServerErrors < maxInternalServerErrors { + continue + } + } + + // for any other error, there's not much we can do automatically + break + } + + return resp, fmt.Errorf("request to %s failed after %d attempts: %v", + endpoint, attempts, err) +} + +// httpReq robustly performs an HTTP request using the given method to the given endpoint, honoring +// the given context's cancellation. The joseJSONPayload is optional; if not nil, it is expected to +// be a JOSE+JSON encoding. The output is also optional; if not nil, the response body will be read +// into output. If the response Content-Type is JSON, it will be JSON-decoded into output, which +// must be a pointer type. If the response is any other Content-Type and if output is a io.Writer, +// it will be written (without interpretation or decoding) to output. In all cases, the returned +// response value will have the body drained and closed, so there is no need to close it again. +// +// If there are any network or I/O errors, the request will be retried as safely and resiliently as +// possible. +func (c *Client) httpReq(ctx context.Context, method, endpoint string, joseJSONPayload []byte, output interface{}) (*http.Response, error) { + // even if the caller doesn't specify an output, we still use a + // buffer to store possible error response (we reset it later) + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + + var resp *http.Response + var err error + + // potentially retry the request if there's network, I/O, or server internal errors + const maxAttempts = 3 + for attempt := 0; attempt < maxAttempts; attempt++ { + if attempt > 0 { + // traffic calming ahead + select { + case <-time.After(250 * time.Millisecond): + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + var body io.Reader + if joseJSONPayload != nil { + body = bytes.NewReader(joseJSONPayload) + } + + var req *http.Request + req, err = http.NewRequestWithContext(ctx, method, endpoint, body) + if err != nil { + return nil, fmt.Errorf("creating request: %w", err) + } + if len(joseJSONPayload) > 0 { + req.Header.Set("Content-Type", "application/jose+json") + } + + // on first attempt, we need to reset buf since it + // came from the pool; after first attempt, we should + // still reset it because we might be retrying after + // a partial download + buf.Reset() + + var retry bool + resp, retry, err = c.doHTTPRequest(req, buf) + if err != nil { + if retry { + if c.Logger != nil { + c.Logger.Warn("HTTP request failed; retrying", + zap.String("url", req.URL.String()), + zap.Error(err)) + } + continue + } + break + } + + // check for HTTP errors + switch { + case resp.StatusCode >= 200 && resp.StatusCode < 300: // OK + case resp.StatusCode >= 400 && resp.StatusCode < 600: // error + if parseMediaType(resp) == "application/problem+json" { + // "When the server responds with an error status, it SHOULD provide + // additional information using a problem document [RFC7807]." (§6.7) + var problem Problem + err = json.Unmarshal(buf.Bytes(), &problem) + if err != nil { + return resp, fmt.Errorf("HTTP %d: JSON-decoding problem details: %w (raw='%s')", + resp.StatusCode, err, buf.String()) + } + if resp.StatusCode >= 500 && joseJSONPayload == nil { + // a 5xx status is probably safe to retry on even after a + // request that had no I/O errors; it could be that the + // server just had a hiccup... so try again, but only if + // there is no request body, because we can't replay a + // request that has an anti-replay nonce, obviously + err = problem + continue + } + return resp, problem + } + return resp, fmt.Errorf("HTTP %d: %s", resp.StatusCode, buf.String()) + default: // what even is this + return resp, fmt.Errorf("unexpected status code: HTTP %d", resp.StatusCode) + } + + // do not retry if we got this far (success) + break + } + if err != nil { + return resp, err + } + + // if expecting a body, finally decode it + if output != nil { + contentType := parseMediaType(resp) + switch contentType { + case "application/json": + // unmarshal JSON + err = json.Unmarshal(buf.Bytes(), output) + if err != nil { + return resp, fmt.Errorf("JSON-decoding response body: %w", err) + } + + default: + // don't interpret anything else here; just hope + // it's a Writer and copy the bytes + w, ok := output.(io.Writer) + if !ok { + return resp, fmt.Errorf("response Content-Type is %s but target container is not io.Writer: %T", contentType, output) + } + _, err = io.Copy(w, buf) + if err != nil { + return resp, err + } + } + } + + return resp, nil +} + +// doHTTPRequest performs an HTTP request at most one time. It returns the response +// (with drained and closed body), having drained any request body into buf. If +// retry == true is returned, then the request should be safe to retry in the case +// of an error. However, in some cases a retry may be recommended even if part of +// the response body has been read and written into buf. Thus, the buffer may have +// been partially written to and should be reset before being reused. +// +// This method remembers any nonce returned by the server. +func (c *Client) doHTTPRequest(req *http.Request, buf *bytes.Buffer) (resp *http.Response, retry bool, err error) { + req.Header.Set("User-Agent", c.userAgent()) + + resp, err = c.httpClient().Do(req) + if err != nil { + return resp, true, fmt.Errorf("performing request: %w", err) + } + defer resp.Body.Close() + + if c.Logger != nil { + c.Logger.Debug("http request", + zap.String("method", req.Method), + zap.String("url", req.URL.String()), + zap.Reflect("headers", req.Header), + zap.Int("status_code", resp.StatusCode), + zap.Reflect("response_headers", resp.Header)) + } + + // "The server MUST include a Replay-Nonce header field + // in every successful response to a POST request and + // SHOULD provide it in error responses as well." §6.5 + // + // "Before sending a POST request to the server, an ACME + // client needs to have a fresh anti-replay nonce to put + // in the 'nonce' header of the JWS. In most cases, the + // client will have gotten a nonce from a previous + // request." §7.2 + // + // So basically, we need to remember the nonces we get + // and use them at the next opportunity. + c.nonces.push(resp.Header.Get(replayNonce)) + + // drain the response body, even if we aren't keeping it + // (this allows us to reuse the connection and also read + // any error information) + _, err = io.Copy(buf, resp.Body) + if err != nil { + // this is likely a network or I/O error, but is it worth retrying? + // technically the request has already completed, it was just our + // download of the response that failed; so we probably should not + // retry if the request succeeded... however, if there was an HTTP + // error, it likely didn't count against any server-enforced rate + // limits, and we DO want to know the error information, so it should + // be safe to retry the request in those cases AS LONG AS there is + // no request body, which in the context of ACME likely includes an + // anti-replay nonce, which obviously we can't reuse + retry = resp.StatusCode >= 400 && req.Body == nil + return resp, retry, fmt.Errorf("reading response body: %w", err) + } + + return resp, false, nil +} + +func (c *Client) httpClient() *http.Client { + if c.HTTPClient == nil { + return http.DefaultClient + } + return c.HTTPClient +} + +func (c *Client) userAgent() string { + ua := fmt.Sprintf("acmez (%s; %s)", runtime.GOOS, runtime.GOARCH) + if c.UserAgent != "" { + ua = c.UserAgent + " " + ua + } + return ua +} + +// extractLinks extracts the URL from the Link header with the +// designated relation rel. It may return more than value +// if there are multiple matching Link values. +// +// Originally by Isaac: https://github.com/eggsampler/acme +// and has been modified to support multiple matching Links. +func extractLinks(resp *http.Response, rel string) []string { + if resp == nil { + return nil + } + var links []string + for _, l := range resp.Header["Link"] { + matches := linkRegex.FindAllStringSubmatch(l, -1) + for _, m := range matches { + if len(m) != 3 { + continue + } + if m[2] == rel { + links = append(links, m[1]) + } + } + } + return links +} + +// parseMediaType returns only the media type from the +// Content-Type header of resp. +func parseMediaType(resp *http.Response) string { + if resp == nil { + return "" + } + ct := resp.Header.Get("Content-Type") + sep := strings.Index(ct, ";") + if sep < 0 { + return ct + } + return strings.TrimSpace(ct[:sep]) +} + +// retryAfter returns a duration from the response's Retry-After +// header field, if it exists. It can return an error if the +// header contains an invalid value. If there is no error but +// there is no Retry-After header provided, then the fallback +// duration is returned instead. +func retryAfter(resp *http.Response, fallback time.Duration) (time.Duration, error) { + if resp == nil { + return fallback, nil + } + raSeconds := resp.Header.Get("Retry-After") + if raSeconds == "" { + return fallback, nil + } + ra, err := strconv.Atoi(raSeconds) + if err != nil || ra < 0 { + return 0, fmt.Errorf("response had invalid Retry-After header: %s", raSeconds) + } + return time.Duration(ra) * time.Second, nil +} + +var bufPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + +var linkRegex = regexp.MustCompile(`<(.+?)>;\s*rel="(.+?)"`) diff --git a/vendor/github.com/mholt/acmez/acme/jws.go b/vendor/github.com/mholt/acmez/acme/jws.go new file mode 100644 index 0000000..bdbb457 --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/jws.go @@ -0,0 +1,263 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// --- ORIGINAL LICENSE --- +// +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the THIRD-PARTY file. +// +// (This file has been modified from its original contents.) +// (And it has dragons. Don't wake the dragons.) + +package acme + +import ( + "crypto" + "crypto/ecdsa" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + _ "crypto/sha512" // need for EC keys + "encoding/base64" + "encoding/json" + "fmt" + "math/big" +) + +var errUnsupportedKey = fmt.Errorf("unknown key type; only RSA and ECDSA are supported") + +// keyID is the account identity provided by a CA during registration. +type keyID string + +// noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID. +// See jwsEncodeJSON for details. +const noKeyID = keyID("") + +// // noPayload indicates jwsEncodeJSON will encode zero-length octet string +// // in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make +// // authenticated GET requests via POSTing with an empty payload. +// // See https://tools.ietf.org/html/rfc8555#section-6.3 for more details. +// const noPayload = "" + +// jwsEncodeEAB creates a JWS payload for External Account Binding according to RFC 8555 §7.3.4. +func jwsEncodeEAB(accountKey crypto.PublicKey, hmacKey []byte, kid keyID, url string) ([]byte, error) { + // §7.3.4: "The 'alg' field MUST indicate a MAC-based algorithm" + alg, sha := "HS256", crypto.SHA256 + + // §7.3.4: "The 'nonce' field MUST NOT be present" + phead, err := jwsHead(alg, "", url, kid, nil) + if err != nil { + return nil, err + } + + encodedKey, err := jwkEncode(accountKey) + if err != nil { + return nil, err + } + payload := base64.RawURLEncoding.EncodeToString([]byte(encodedKey)) + + payloadToSign := []byte(phead + "." + payload) + + h := hmac.New(sha256.New, hmacKey) + h.Write(payloadToSign) + sig := h.Sum(nil) + + return jwsFinal(sha, sig, phead, payload) +} + +// jwsEncodeJSON signs claimset using provided key and a nonce. +// The result is serialized in JSON format containing either kid or jwk +// fields based on the provided keyID value. +// +// If kid is non-empty, its quoted value is inserted in the protected head +// as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted +// as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive. +// +// See https://tools.ietf.org/html/rfc7515#section-7. +// +// If nonce is empty, it will not be encoded into the header. +func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) { + alg, sha := jwsHasher(key.Public()) + if alg == "" || !sha.Available() { + return nil, errUnsupportedKey + } + + phead, err := jwsHead(alg, nonce, url, kid, key) + if err != nil { + return nil, err + } + + var payload string + if claimset != nil { + cs, err := json.Marshal(claimset) + if err != nil { + return nil, err + } + payload = base64.RawURLEncoding.EncodeToString(cs) + } + + payloadToSign := []byte(phead + "." + payload) + hash := sha.New() + _, _ = hash.Write(payloadToSign) + digest := hash.Sum(nil) + + sig, err := jwsSign(key, sha, digest) + if err != nil { + return nil, err + } + + return jwsFinal(sha, sig, phead, payload) +} + +// jwkEncode encodes public part of an RSA or ECDSA key into a JWK. +// The result is also suitable for creating a JWK thumbprint. +// https://tools.ietf.org/html/rfc7517 +func jwkEncode(pub crypto.PublicKey) (string, error) { + switch pub := pub.(type) { + case *rsa.PublicKey: + // https://tools.ietf.org/html/rfc7518#section-6.3.1 + n := pub.N + e := big.NewInt(int64(pub.E)) + // Field order is important. + // See https://tools.ietf.org/html/rfc7638#section-3.3 for details. + return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, + base64.RawURLEncoding.EncodeToString(e.Bytes()), + base64.RawURLEncoding.EncodeToString(n.Bytes()), + ), nil + case *ecdsa.PublicKey: + // https://tools.ietf.org/html/rfc7518#section-6.2.1 + p := pub.Curve.Params() + n := p.BitSize / 8 + if p.BitSize%8 != 0 { + n++ + } + x := pub.X.Bytes() + if n > len(x) { + x = append(make([]byte, n-len(x)), x...) + } + y := pub.Y.Bytes() + if n > len(y) { + y = append(make([]byte, n-len(y)), y...) + } + // Field order is important. + // See https://tools.ietf.org/html/rfc7638#section-3.3 for details. + return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, + p.Name, + base64.RawURLEncoding.EncodeToString(x), + base64.RawURLEncoding.EncodeToString(y), + ), nil + } + return "", errUnsupportedKey +} + +// jwsHead constructs the protected JWS header for the given fields. +// Since jwk and kid are mutually-exclusive, the jwk will be encoded +// only if kid is empty. If nonce is empty, it will not be encoded. +func jwsHead(alg, nonce, url string, kid keyID, key crypto.Signer) (string, error) { + phead := fmt.Sprintf(`{"alg":%q`, alg) + if kid == noKeyID { + jwk, err := jwkEncode(key.Public()) + if err != nil { + return "", err + } + phead += fmt.Sprintf(`,"jwk":%s`, jwk) + } else { + phead += fmt.Sprintf(`,"kid":%q`, kid) + } + if nonce != "" { + phead += fmt.Sprintf(`,"nonce":%q`, nonce) + } + phead += fmt.Sprintf(`,"url":%q}`, url) + phead = base64.RawURLEncoding.EncodeToString([]byte(phead)) + return phead, nil +} + +// jwsFinal constructs the final JWS object. +func jwsFinal(sha crypto.Hash, sig []byte, phead, payload string) ([]byte, error) { + enc := struct { + Protected string `json:"protected"` + Payload string `json:"payload"` + Sig string `json:"signature"` + }{ + Protected: phead, + Payload: payload, + Sig: base64.RawURLEncoding.EncodeToString(sig), + } + result, err := json.Marshal(&enc) + if err != nil { + return nil, err + } + return result, nil +} + +// jwsSign signs the digest using the given key. +// The hash is unused for ECDSA keys. +// +// Note: non-stdlib crypto.Signer implementations are expected to return +// the signature in the format as specified in RFC7518. +// See https://tools.ietf.org/html/rfc7518 for more details. +func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) { + if key, ok := key.(*ecdsa.PrivateKey); ok { + // The key.Sign method of ecdsa returns ASN1-encoded signature. + // So, we use the package Sign function instead + // to get R and S values directly and format the result accordingly. + r, s, err := ecdsa.Sign(rand.Reader, key, digest) + if err != nil { + return nil, err + } + rb, sb := r.Bytes(), s.Bytes() + size := key.Params().BitSize / 8 + if size%8 > 0 { + size++ + } + sig := make([]byte, size*2) + copy(sig[size-len(rb):], rb) + copy(sig[size*2-len(sb):], sb) + return sig, nil + } + return key.Sign(rand.Reader, digest, hash) +} + +// jwsHasher indicates suitable JWS algorithm name and a hash function +// to use for signing a digest with the provided key. +// It returns ("", 0) if the key is not supported. +func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) { + switch pub := pub.(type) { + case *rsa.PublicKey: + return "RS256", crypto.SHA256 + case *ecdsa.PublicKey: + switch pub.Params().Name { + case "P-256": + return "ES256", crypto.SHA256 + case "P-384": + return "ES384", crypto.SHA384 + case "P-521": + return "ES512", crypto.SHA512 + } + } + return "", 0 +} + +// jwkThumbprint creates a JWK thumbprint out of pub +// as specified in https://tools.ietf.org/html/rfc7638. +func jwkThumbprint(pub crypto.PublicKey) (string, error) { + jwk, err := jwkEncode(pub) + if err != nil { + return "", err + } + b := sha256.Sum256([]byte(jwk)) + return base64.RawURLEncoding.EncodeToString(b[:]), nil +} diff --git a/vendor/github.com/mholt/acmez/acme/order.go b/vendor/github.com/mholt/acmez/acme/order.go new file mode 100644 index 0000000..579bb3a --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/order.go @@ -0,0 +1,247 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acme + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "time" +) + +// Order is an object that "represents a client's request for a certificate +// and is used to track the progress of that order through to issuance. +// Thus, the object contains information about the requested +// certificate, the authorizations that the server requires the client +// to complete, and any certificates that have resulted from this order." +// §7.1.3 +type Order struct { + // status (required, string): The status of this order. Possible + // values are "pending", "ready", "processing", "valid", and + // "invalid". See Section 7.1.6. + Status string `json:"status"` + + // expires (optional, string): The timestamp after which the server + // will consider this order invalid, encoded in the format specified + // in [RFC3339]. This field is REQUIRED for objects with "pending" + // or "valid" in the status field. + Expires time.Time `json:"expires,omitempty"` + + // identifiers (required, array of object): An array of identifier + // objects that the order pertains to. + Identifiers []Identifier `json:"identifiers"` + + // notBefore (optional, string): The requested value of the notBefore + // field in the certificate, in the date format defined in [RFC3339]. + NotBefore *time.Time `json:"notBefore,omitempty"` + + // notAfter (optional, string): The requested value of the notAfter + // field in the certificate, in the date format defined in [RFC3339]. + NotAfter *time.Time `json:"notAfter,omitempty"` + + // error (optional, object): The error that occurred while processing + // the order, if any. This field is structured as a problem document + // [RFC7807]. + Error *Problem `json:"error,omitempty"` + + // authorizations (required, array of string): For pending orders, the + // authorizations that the client needs to complete before the + // requested certificate can be issued (see Section 7.5), including + // unexpired authorizations that the client has completed in the past + // for identifiers specified in the order. The authorizations + // required are dictated by server policy; there may not be a 1:1 + // relationship between the order identifiers and the authorizations + // required. For final orders (in the "valid" or "invalid" state), + // the authorizations that were completed. Each entry is a URL from + // which an authorization can be fetched with a POST-as-GET request. + Authorizations []string `json:"authorizations"` + + // finalize (required, string): A URL that a CSR must be POSTed to once + // all of the order's authorizations are satisfied to finalize the + // order. The result of a successful finalization will be the + // population of the certificate URL for the order. + Finalize string `json:"finalize"` + + // certificate (optional, string): A URL for the certificate that has + // been issued in response to this order. + Certificate string `json:"certificate"` + + // Similar to new-account, the server returns a 201 response with + // the URL to the order object in the Location header. + // + // We transfer the value from the header to this field for + // storage and recall purposes. + Location string `json:"-"` +} + +// Identifier is used in order and authorization (authz) objects. +type Identifier struct { + // type (required, string): The type of identifier. This document + // defines the "dns" identifier type. See the registry defined in + // Section 9.7.7 for any others. + Type string `json:"type"` + + // value (required, string): The identifier itself. + Value string `json:"value"` +} + +// NewOrder creates a new order with the server. +// +// "The client begins the certificate issuance process by sending a POST +// request to the server's newOrder resource." §7.4 +func (c *Client) NewOrder(ctx context.Context, account Account, order Order) (Order, error) { + if err := c.provision(ctx); err != nil { + return order, err + } + resp, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, c.dir.NewOrder, order, &order) + if err != nil { + return order, err + } + order.Location = resp.Header.Get("Location") + return order, nil +} + +// FinalizeOrder finalizes the order with the server and polls under the server has +// updated the order status. The CSR must be in ASN.1 DER-encoded format. If this +// succeeds, the certificate is ready to download once this returns. +// +// "Once the client believes it has fulfilled the server's requirements, +// it should send a POST request to the order resource's finalize URL." §7.4 +func (c *Client) FinalizeOrder(ctx context.Context, account Account, order Order, csrASN1DER []byte) (Order, error) { + if err := c.provision(ctx); err != nil { + return order, err + } + + body := struct { + // csr (required, string): A CSR encoding the parameters for the + // certificate being requested [RFC2986]. The CSR is sent in the + // base64url-encoded version of the DER format. (Note: Because this + // field uses base64url, and does not include headers, it is + // different from PEM.) §7.4 + CSR string `json:"csr"` + }{ + CSR: base64.RawURLEncoding.EncodeToString(csrASN1DER), + } + + resp, err := c.httpPostJWS(ctx, account.PrivateKey, account.Location, order.Finalize, body, &order) + if err != nil { + // "A request to finalize an order will result in error if the order is + // not in the 'ready' state. In such cases, the server MUST return a + // 403 (Forbidden) error with a problem document of type + // 'orderNotReady'. The client should then send a POST-as-GET request + // to the order resource to obtain its current state. The status of the + // order will indicate what action the client should take (see below)." + // §7.4 + var problem Problem + if errors.As(err, &problem) { + if problem.Type != ProblemTypeOrderNotReady { + return order, err + } + } else { + return order, err + } + } + + // unlike with accounts and authorizations, the spec isn't clear on whether + // the server MUST set this on finalizing the order, but their example shows a + // Location header, so I guess if it's set in the response, we should keep it + if newLocation := resp.Header.Get("Location"); newLocation != "" { + order.Location = newLocation + } + + if finished, err := orderIsFinished(order); finished { + return order, err + } + + // TODO: "The elements of the "authorizations" and "identifiers" arrays are + // immutable once set. If a client observes a change + // in the contents of either array, then it SHOULD consider the order + // invalid." + + maxDuration := c.pollTimeout() + start := time.Now() + for time.Since(start) < maxDuration { + // querying an order is expensive on the server-side, so we + // shouldn't do it too frequently; honor server preference + interval, err := retryAfter(resp, c.pollInterval()) + if err != nil { + return order, err + } + select { + case <-time.After(interval): + case <-ctx.Done(): + return order, ctx.Err() + } + + resp, err = c.httpPostJWS(ctx, account.PrivateKey, account.Location, order.Location, nil, &order) + if err != nil { + return order, fmt.Errorf("polling order status: %w", err) + } + + // (same reasoning as above) + if newLocation := resp.Header.Get("Location"); newLocation != "" { + order.Location = newLocation + } + + if finished, err := orderIsFinished(order); finished { + return order, err + } + } + + return order, fmt.Errorf("order took too long") +} + +// orderIsFinished returns true if the order processing is complete, +// regardless of success or failure. If this function returns true, +// polling an order status should stop. If there is an error with the +// order, an error will be returned. This function should be called +// only after a request to finalize an order. See §7.4. +func orderIsFinished(order Order) (bool, error) { + switch order.Status { + case StatusInvalid: + // "invalid": The certificate will not be issued. Consider this + // order process abandoned. + return true, fmt.Errorf("final order is invalid: %w", order.Error) + + case StatusPending: + // "pending": The server does not believe that the client has + // fulfilled the requirements. Check the "authorizations" array for + // entries that are still pending. + return true, fmt.Errorf("order pending, authorizations remaining: %v", order.Authorizations) + + case StatusReady: + // "ready": The server agrees that the requirements have been + // fulfilled, and is awaiting finalization. Submit a finalization + // request. + // (we did just submit a finalization request, so this is an error) + return true, fmt.Errorf("unexpected state: %s - order already finalized", order.Status) + + case StatusProcessing: + // "processing": The certificate is being issued. Send a GET request + // after the time given in the "Retry-After" header field of the + // response, if any. + return false, nil + + case StatusValid: + // "valid": The server has issued the certificate and provisioned its + // URL to the "certificate" field of the order. Download the + // certificate. + return true, nil + + default: + return true, fmt.Errorf("unrecognized order status: %s", order.Status) + } +} diff --git a/vendor/github.com/mholt/acmez/acme/problem.go b/vendor/github.com/mholt/acmez/acme/problem.go new file mode 100644 index 0000000..98fdb00 --- /dev/null +++ b/vendor/github.com/mholt/acmez/acme/problem.go @@ -0,0 +1,136 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acme + +import "fmt" + +// Problem carries the details of an error from HTTP APIs as +// defined in RFC 7807: https://tools.ietf.org/html/rfc7807 +// and as extended by RFC 8555 §6.7: +// https://tools.ietf.org/html/rfc8555#section-6.7 +type Problem struct { + // "type" (string) - A URI reference [RFC3986] that identifies the + // problem type. This specification encourages that, when + // dereferenced, it provide human-readable documentation for the + // problem type (e.g., using HTML [W3C.REC-html5-20141028]). When + // this member is not present, its value is assumed to be + // "about:blank". §3.1 + Type string `json:"type"` + + // "title" (string) - A short, human-readable summary of the problem + // type. It SHOULD NOT change from occurrence to occurrence of the + // problem, except for purposes of localization (e.g., using + // proactive content negotiation; see [RFC7231], Section 3.4). §3.1 + Title string `json:"title,omitempty"` + + // "status" (number) - The HTTP status code ([RFC7231], Section 6) + // generated by the origin server for this occurrence of the problem. + // §3.1 + Status int `json:"status,omitempty"` + + // "detail" (string) - A human-readable explanation specific to this + // occurrence of the problem. §3.1 + // + // RFC 8555 §6.7: "Clients SHOULD display the 'detail' field of all + // errors." + Detail string `json:"detail,omitempty"` + + // "instance" (string) - A URI reference that identifies the specific + // occurrence of the problem. It may or may not yield further + // information if dereferenced. §3.1 + Instance string `json:"instance,omitempty"` + + // "Sometimes a CA may need to return multiple errors in response to a + // request. Additionally, the CA may need to attribute errors to + // specific identifiers. For instance, a newOrder request may contain + // multiple identifiers for which the CA cannot issue certificates. In + // this situation, an ACME problem document MAY contain the + // 'subproblems' field, containing a JSON array of problem documents." + // RFC 8555 §6.7.1 + Subproblems []Subproblem `json:"subproblems,omitempty"` + + // For convenience, we've added this field to associate with a value + // that is related to or caused the problem. It is not part of the + // spec, but, if a challenge fails for example, we can associate the + // error with the problematic authz object by setting this field. + // Challenge failures will have this set to an Authorization type. + Resource interface{} `json:"-"` +} + +func (p Problem) Error() string { + // TODO: 7.3.3: Handle changes to Terms of Service (notice it uses the Instance field and Link header) + + // RFC 8555 §6.7: "Clients SHOULD display the 'detail' field of all errors." + s := fmt.Sprintf("HTTP %d %s - %s", p.Status, p.Type, p.Detail) + if len(p.Subproblems) > 0 { + for _, v := range p.Subproblems { + s += fmt.Sprintf(", problem %q: %s", v.Type, v.Detail) + } + } + if p.Instance != "" { + s += ", url: " + p.Instance + } + return s +} + +// Subproblem describes a more specific error in a problem according to +// RFC 8555 §6.7.1: "An ACME problem document MAY contain the +// 'subproblems' field, containing a JSON array of problem documents, +// each of which MAY contain an 'identifier' field." +type Subproblem struct { + Problem + + // "If present, the 'identifier' field MUST contain an ACME + // identifier (Section 9.7.7)." §6.7.1 + Identifier Identifier `json:"identifier,omitempty"` +} + +// Standard token values for the "type" field of problems, as defined +// in RFC 8555 §6.7: https://tools.ietf.org/html/rfc8555#section-6.7 +// +// "To facilitate automatic response to errors, this document defines the +// following standard tokens for use in the 'type' field (within the +// ACME URN namespace 'urn:ietf:params:acme:error:') ... This list is not +// exhaustive. The server MAY return errors whose 'type' field is set to +// a URI other than those defined above." +const ( + // The ACME error URN prefix. + ProblemTypeNamespace = "urn:ietf:params:acme:error:" + + ProblemTypeAccountDoesNotExist = ProblemTypeNamespace + "accountDoesNotExist" + ProblemTypeAlreadyRevoked = ProblemTypeNamespace + "alreadyRevoked" + ProblemTypeBadCSR = ProblemTypeNamespace + "badCSR" + ProblemTypeBadNonce = ProblemTypeNamespace + "badNonce" + ProblemTypeBadPublicKey = ProblemTypeNamespace + "badPublicKey" + ProblemTypeBadRevocationReason = ProblemTypeNamespace + "badRevocationReason" + ProblemTypeBadSignatureAlgorithm = ProblemTypeNamespace + "badSignatureAlgorithm" + ProblemTypeCAA = ProblemTypeNamespace + "caa" + ProblemTypeCompound = ProblemTypeNamespace + "compound" + ProblemTypeConnection = ProblemTypeNamespace + "connection" + ProblemTypeDNS = ProblemTypeNamespace + "dns" + ProblemTypeExternalAccountRequired = ProblemTypeNamespace + "externalAccountRequired" + ProblemTypeIncorrectResponse = ProblemTypeNamespace + "incorrectResponse" + ProblemTypeInvalidContact = ProblemTypeNamespace + "invalidContact" + ProblemTypeMalformed = ProblemTypeNamespace + "malformed" + ProblemTypeOrderNotReady = ProblemTypeNamespace + "orderNotReady" + ProblemTypeRateLimited = ProblemTypeNamespace + "rateLimited" + ProblemTypeRejectedIdentifier = ProblemTypeNamespace + "rejectedIdentifier" + ProblemTypeServerInternal = ProblemTypeNamespace + "serverInternal" + ProblemTypeTLS = ProblemTypeNamespace + "tls" + ProblemTypeUnauthorized = ProblemTypeNamespace + "unauthorized" + ProblemTypeUnsupportedContact = ProblemTypeNamespace + "unsupportedContact" + ProblemTypeUnsupportedIdentifier = ProblemTypeNamespace + "unsupportedIdentifier" + ProblemTypeUserActionRequired = ProblemTypeNamespace + "userActionRequired" +) diff --git a/vendor/github.com/mholt/acmez/client.go b/vendor/github.com/mholt/acmez/client.go new file mode 100644 index 0000000..4cad9c5 --- /dev/null +++ b/vendor/github.com/mholt/acmez/client.go @@ -0,0 +1,656 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package acmez implements the higher-level flow of the ACME specification, +// RFC 8555: https://tools.ietf.org/html/rfc8555, specifically the sequence +// in Section 7.1 (page 21). +// +// It makes it easy to obtain certificates with various challenge types +// using pluggable challenge solvers, and provides some handy utilities for +// implementing solvers and using the certificates. It DOES NOT manage +// certificates, it only gets them from the ACME server. +// +// NOTE: This package's main function is to get a certificate, not manage it. +// Most users will want to *manage* certificates over the lifetime of a +// long-running program such as a HTTPS or TLS server, and should use CertMagic +// instead: https://github.com/caddyserver/certmagic. +package acmez + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/x509" + "errors" + "fmt" + weakrand "math/rand" + "net" + "net/url" + "sort" + "strings" + "sync" + "time" + + "github.com/mholt/acmez/acme" + "go.uber.org/zap" + "golang.org/x/net/idna" +) + +func init() { + weakrand.Seed(time.Now().UnixNano()) +} + +// Client is a high-level API for ACME operations. It wraps +// a lower-level ACME client with useful functions to make +// common flows easier, especially for the issuance of +// certificates. +type Client struct { + *acme.Client + + // Map of solvers keyed by name of the challenge type. + ChallengeSolvers map[string]Solver + + // An optional logger. Default: no logs + Logger *zap.Logger +} + +// ObtainCertificateUsingCSR obtains all resulting certificate chains using the given CSR, which +// must be completely and properly filled out (particularly its DNSNames and Raw fields - this +// usually involves creating a template CSR, then calling x509.CreateCertificateRequest, then +// x509.ParseCertificateRequest on the output). The Subject CommonName is NOT considered. +// +// It implements every single part of the ACME flow described in RFC 8555 §7.1 with the exception +// of "Create account" because this method signature does not have a way to return the udpated +// account object. The account's status MUST be "valid" in order to succeed. +// +// As far as SANs go, this method currently only supports DNSNames on the csr. +func (c *Client) ObtainCertificateUsingCSR(ctx context.Context, account acme.Account, csr *x509.CertificateRequest) ([]acme.Certificate, error) { + if account.Status != acme.StatusValid { + return nil, fmt.Errorf("account status is not valid: %s", account.Status) + } + if csr == nil { + return nil, fmt.Errorf("missing CSR") + } + + var ids []acme.Identifier + for _, name := range csr.DNSNames { + // "The domain name MUST be encoded in the form in which it would appear + // in a certificate. That is, it MUST be encoded according to the rules + // in Section 7 of [RFC5280]." §7.1.4 + normalizedName, err := idna.ToASCII(name) + if err != nil { + return nil, fmt.Errorf("converting identifier '%s' to ASCII: %v", name, err) + } + + ids = append(ids, acme.Identifier{ + Type: "dns", + Value: normalizedName, + }) + } + if len(ids) == 0 { + return nil, fmt.Errorf("no identifiers found") + } + + order := acme.Order{Identifiers: ids} + var err error + + // remember which challenge types failed for which identifiers + // so we can retry with other challenge types + failedChallengeTypes := make(failedChallengeMap) + + const maxAttempts = 3 // hard cap on number of retries for good measure + for attempt := 1; attempt <= maxAttempts; attempt++ { + if attempt > 1 { + select { + case <-time.After(1 * time.Second): + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + // create order for a new certificate + order, err = c.Client.NewOrder(ctx, account, order) + if err != nil { + return nil, fmt.Errorf("creating new order: %w", err) + } + + // solve one challenge for each authz on the order + err = c.solveChallenges(ctx, account, order, failedChallengeTypes) + + // yay, we win! + if err == nil { + break + } + + // for some errors, we can retry with different challenge types + var problem acme.Problem + if errors.As(err, &problem) { + authz := problem.Resource.(acme.Authorization) + if c.Logger != nil { + c.Logger.Error("validating authorization", + zap.String("identifier", authz.IdentifierValue()), + zap.Error(err), + zap.String("order", order.Location), + zap.Int("attempt", attempt), + zap.Int("max_attempts", maxAttempts)) + } + err = fmt.Errorf("solving challenge: %s: %w", authz.IdentifierValue(), err) + if errors.As(err, &retryableErr{}) { + continue + } + return nil, err + } + + return nil, fmt.Errorf("solving challenges: %w (order=%s)", err, order.Location) + } + + if c.Logger != nil { + c.Logger.Info("validations succeeded; finalizing order", zap.String("order", order.Location)) + } + + // finalize the order, which requests the CA to issue us a certificate + order, err = c.Client.FinalizeOrder(ctx, account, order, csr.Raw) + if err != nil { + return nil, fmt.Errorf("finalizing order %s: %w", order.Location, err) + } + + // finally, download the certificate + certChains, err := c.Client.GetCertificateChain(ctx, account, order.Certificate) + if err != nil { + return nil, fmt.Errorf("downloading certificate chain from %s: %w (order=%s)", + order.Certificate, err, order.Location) + } + + if c.Logger != nil { + if len(certChains) == 0 { + c.Logger.Info("no certificate chains offered by server") + } else { + c.Logger.Info("successfully downloaded available certificate chains", + zap.Int("count", len(certChains)), + zap.String("first_url", certChains[0].URL)) + } + } + + return certChains, nil +} + +// ObtainCertificate is the same as ObtainCertificateUsingCSR, except it is a slight wrapper +// that generates the CSR for you. Doing so requires the private key you will be using for +// the certificate (different from the account private key). It obtains a certificate for +// the given SANs (domain names) using the provided account. +func (c *Client) ObtainCertificate(ctx context.Context, account acme.Account, certPrivateKey crypto.Signer, sans []string) ([]acme.Certificate, error) { + if len(sans) == 0 { + return nil, fmt.Errorf("no DNS names provided: %v", sans) + } + if certPrivateKey == nil { + return nil, fmt.Errorf("missing certificate private key") + } + + csrTemplate := new(x509.CertificateRequest) + for _, name := range sans { + if ip := net.ParseIP(name); ip != nil { + csrTemplate.IPAddresses = append(csrTemplate.IPAddresses, ip) + } else if strings.Contains(name, "@") { + csrTemplate.EmailAddresses = append(csrTemplate.EmailAddresses, name) + } else if u, err := url.Parse(name); err == nil && strings.Contains(name, "/") { + csrTemplate.URIs = append(csrTemplate.URIs, u) + } else { + csrTemplate.DNSNames = append(csrTemplate.DNSNames, name) + } + } + + // to properly fill out the CSR, we need to create it, then parse it + csrDER, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, certPrivateKey) + if err != nil { + return nil, fmt.Errorf("generating CSR: %v", err) + } + csr, err := x509.ParseCertificateRequest(csrDER) + if err != nil { + return nil, fmt.Errorf("parsing generated CSR: %v", err) + } + + return c.ObtainCertificateUsingCSR(ctx, account, csr) +} + +// getAuthzObjects constructs stateful authorization objects for each authz on the order. +// It includes all authorizations regardless of their status so that they can be +// deactivated at the end if necessary. Be sure to check authz status before operating +// on the authz; not all will be "pending" - some authorizations might already be valid. +func (c *Client) getAuthzObjects(ctx context.Context, account acme.Account, order acme.Order, + failedChallengeTypes failedChallengeMap) ([]*authzState, error) { + var authzStates []*authzState + var err error + + // start by allowing each authz's solver to present for its challenge + for _, authzURL := range order.Authorizations { + authz := &authzState{account: account} + authz.Authorization, err = c.Client.GetAuthorization(ctx, account, authzURL) + if err != nil { + return nil, fmt.Errorf("getting authorization at %s: %w", authzURL, err) + } + + // add all offered challenge types to our memory if they + // arent't there already; we use this for statistics to + // choose the most successful challenge type over time; + // if initial fill, randomize challenge order + preferredChallengesMu.Lock() + preferredWasEmpty := len(preferredChallenges) == 0 + for _, chal := range authz.Challenges { + preferredChallenges.addUnique(chal.Type) + } + if preferredWasEmpty { + weakrand.Shuffle(len(preferredChallenges), func(i, j int) { + preferredChallenges[i], preferredChallenges[j] = + preferredChallenges[j], preferredChallenges[i] + }) + } + preferredChallengesMu.Unlock() + + // copy over any challenges that are not known to have already + // failed, making them candidates for solving for this authz + failedChallengeTypes.enqueueUnfailedChallenges(authz) + + authzStates = append(authzStates, authz) + } + + // sort authzs so that challenges which require waiting go first; no point + // in getting authorizations quickly while others will take a long time + sort.SliceStable(authzStates, func(i, j int) bool { + _, iIsWaiter := authzStates[i].currentSolver.(Waiter) + _, jIsWaiter := authzStates[j].currentSolver.(Waiter) + // "if i is a waiter, and j is not a waiter, then i is less than j" + return iIsWaiter && !jIsWaiter + }) + + return authzStates, nil +} + +func (c *Client) solveChallenges(ctx context.Context, account acme.Account, order acme.Order, failedChallengeTypes failedChallengeMap) error { + authzStates, err := c.getAuthzObjects(ctx, account, order, failedChallengeTypes) + if err != nil { + return err + } + + // when the function returns, make sure we clean up any and all resources + defer func() { + // always clean up any remaining challenge solvers + for _, authz := range authzStates { + if authz.currentSolver == nil { + // happens when authz state ended on a challenge we have no + // solver for or if we have already cleaned up this solver + continue + } + if err := authz.currentSolver.CleanUp(ctx, authz.currentChallenge); err != nil { + if c.Logger != nil { + c.Logger.Error("cleaning up solver", + zap.String("identifier", authz.IdentifierValue()), + zap.String("challenge_type", authz.currentChallenge.Type), + zap.Error(err)) + } + } + } + + if err == nil { + return + } + + // if this function returns with an error, make sure to deactivate + // all pending or valid authorization objects so they don't "leak" + // See: https://github.com/go-acme/lego/issues/383 and https://github.com/go-acme/lego/issues/353 + for _, authz := range authzStates { + if authz.Status != acme.StatusPending && authz.Status != acme.StatusValid { + continue + } + updatedAuthz, err := c.Client.DeactivateAuthorization(ctx, account, authz.Location) + if err != nil { + if c.Logger != nil { + c.Logger.Error("deactivating authorization", + zap.String("identifier", authz.IdentifierValue()), + zap.String("authz", authz.Location), + zap.Error(err)) + } + } + authz.Authorization = updatedAuthz + } + }() + + // present for all challenges first; this allows them all to begin any + // slow tasks up front if necessary before we start polling/waiting + for _, authz := range authzStates { + // see §7.1.6 for state transitions + if authz.Status != acme.StatusPending && authz.Status != acme.StatusValid { + return fmt.Errorf("authz %s has unexpected status; order will fail: %s", authz.Location, authz.Status) + } + if authz.Status == acme.StatusValid { + continue + } + + err = c.presentForNextChallenge(ctx, authz) + if err != nil { + return err + } + } + + // now that all solvers have had the opportunity to present, tell + // the server to begin the selected challenge for each authz + for _, authz := range authzStates { + err = c.initiateCurrentChallenge(ctx, authz) + if err != nil { + return err + } + } + + // poll each authz to wait for completion of all challenges + for _, authz := range authzStates { + err = c.pollAuthorization(ctx, account, authz, failedChallengeTypes) + if err != nil { + return err + } + } + + return nil +} + +func (c *Client) presentForNextChallenge(ctx context.Context, authz *authzState) error { + if authz.Status != acme.StatusPending { + if authz.Status == acme.StatusValid && c.Logger != nil { + c.Logger.Info("authorization already valid", + zap.String("identifier", authz.IdentifierValue()), + zap.String("authz_url", authz.Location), + zap.Time("expires", authz.Expires)) + } + return nil + } + + err := c.nextChallenge(authz) + if err != nil { + return err + } + + if c.Logger != nil { + c.Logger.Info("trying to solve challenge", + zap.String("identifier", authz.IdentifierValue()), + zap.String("challenge_type", authz.currentChallenge.Type), + zap.String("ca", c.Directory)) + } + + err = authz.currentSolver.Present(ctx, authz.currentChallenge) + if err != nil { + return fmt.Errorf("presenting for challenge: %w", err) + } + + return nil +} + +func (c *Client) initiateCurrentChallenge(ctx context.Context, authz *authzState) error { + if authz.Status != acme.StatusPending { + return nil + } + + // by now, all challenges should have had an opportunity to present, so + // if this solver needs more time to finish presenting, wait on it now + // (yes, this does block the initiation of the other challenges, but + // that's probably OK, since we can't finalize the order until the slow + // challenges are done too) + if waiter, ok := authz.currentSolver.(Waiter); ok { + err := waiter.Wait(ctx, authz.currentChallenge) + if err != nil { + return fmt.Errorf("waiting for solver %T to be ready: %w", authz.currentSolver, err) + } + } + + // tell the server to initiate the challenge + var err error + authz.currentChallenge, err = c.Client.InitiateChallenge(ctx, authz.account, authz.currentChallenge) + if err != nil { + return fmt.Errorf("initiating challenge with server: %w", err) + } + + if c.Logger != nil { + c.Logger.Debug("challenge accepted", + zap.String("identifier", authz.IdentifierValue()), + zap.String("challenge_type", authz.currentChallenge.Type)) + } + + return nil +} + +// nextChallenge sets the next challenge (and associated solver) on +// authz; it returns an error if there is no compatible challenge. +func (c *Client) nextChallenge(authz *authzState) error { + preferredChallengesMu.Lock() + defer preferredChallengesMu.Unlock() + + // find the most-preferred challenge that is also in the list of + // remaining challenges, then make sure we have a solver for it + for _, prefChalType := range preferredChallenges { + for i, remainingChal := range authz.remainingChallenges { + if remainingChal.Type != prefChalType.typeName { + continue + } + authz.currentChallenge = remainingChal + authz.currentSolver = c.ChallengeSolvers[authz.currentChallenge.Type] + if authz.currentSolver != nil { + authz.remainingChallenges = append(authz.remainingChallenges[:i], authz.remainingChallenges[i+1:]...) + return nil + } + if c.Logger != nil { + c.Logger.Debug("no solver configured", zap.String("challenge_type", remainingChal.Type)) + } + break + } + } + return fmt.Errorf("%s: no solvers available for remaining challenges (configured=%v offered=%v remaining=%v)", + authz.IdentifierValue(), c.enabledChallengeTypes(), authz.listOfferedChallenges(), authz.listRemainingChallenges()) +} + +func (c *Client) pollAuthorization(ctx context.Context, account acme.Account, authz *authzState, failedChallengeTypes failedChallengeMap) error { + // In §7.5.1, the spec says: + // + // "For challenges where the client can tell when the server has + // validated the challenge (e.g., by seeing an HTTP or DNS request + // from the server), the client SHOULD NOT begin polling until it has + // seen the validation request from the server." + // + // However, in practice, this is difficult in the general case because + // we would need to design some relatively-nuanced concurrency and hope + // that the solver implementations also get their side right -- and the + // fact that it's even possible only sometimes makes it harder, because + // each solver needs a way to signal whether we should wait for its + // approval. So no, I've decided not to implement that recommendation + // in this particular library, but any implementations that use the lower + // ACME API directly are welcome and encouraged to do so where possible. + var err error + authz.Authorization, err = c.Client.PollAuthorization(ctx, account, authz.Authorization) + + // if a challenge was attempted (i.e. did not start valid)... + if authz.currentSolver != nil { + // increment the statistics on this challenge type before handling error + preferredChallengesMu.Lock() + preferredChallenges.increment(authz.currentChallenge.Type, err == nil) + preferredChallengesMu.Unlock() + + // always clean up the challenge solver after polling, regardless of error + cleanupErr := authz.currentSolver.CleanUp(ctx, authz.currentChallenge) + if cleanupErr != nil && c.Logger != nil { + c.Logger.Error("cleaning up solver", + zap.String("identifier", authz.IdentifierValue()), + zap.String("challenge_type", authz.currentChallenge.Type), + zap.Error(err)) + } + authz.currentSolver = nil // avoid cleaning it up again later + } + + // finally, handle any error from validating the authz + if err != nil { + var problem acme.Problem + if errors.As(err, &problem) { + if c.Logger != nil { + c.Logger.Error("challenge failed", + zap.String("identifier", authz.IdentifierValue()), + zap.String("challenge_type", authz.currentChallenge.Type), + zap.Int("status_code", problem.Status), + zap.String("problem_type", problem.Type), + zap.String("error", problem.Detail)) + } + + failedChallengeTypes.rememberFailedChallenge(authz) + + switch problem.Type { + case acme.ProblemTypeConnection, + acme.ProblemTypeDNS, + acme.ProblemTypeServerInternal, + acme.ProblemTypeUnauthorized, + acme.ProblemTypeTLS: + // this error might be recoverable with another challenge type + return retryableErr{err} + } + } + return fmt.Errorf("[%s] %w", authz.Authorization.IdentifierValue(), err) + } + return nil +} + +func (c *Client) enabledChallengeTypes() []string { + enabledChallenges := make([]string, 0, len(c.ChallengeSolvers)) + for name, val := range c.ChallengeSolvers { + if val != nil { + enabledChallenges = append(enabledChallenges, name) + } + } + return enabledChallenges +} + +type authzState struct { + acme.Authorization + account acme.Account + currentChallenge acme.Challenge + currentSolver Solver + remainingChallenges []acme.Challenge +} + +func (authz authzState) listOfferedChallenges() []string { + return challengeTypeNames(authz.Challenges) +} + +func (authz authzState) listRemainingChallenges() []string { + return challengeTypeNames(authz.remainingChallenges) +} + +func challengeTypeNames(challengeList []acme.Challenge) []string { + names := make([]string, 0, len(challengeList)) + for _, chal := range challengeList { + names = append(names, chal.Type) + } + return names +} + +// TODO: possibly configurable policy? converge to most successful (current) vs. completely random + +// challengeHistory is a memory of how successful a challenge type is. +type challengeHistory struct { + typeName string + successes, total int +} + +func (ch challengeHistory) successRatio() float64 { + if ch.total == 0 { + return 1.0 + } + return float64(ch.successes) / float64(ch.total) +} + +// failedChallengeMap keeps track of failed challenge types per identifier. +type failedChallengeMap map[string][]string + +func (fcm failedChallengeMap) rememberFailedChallenge(authz *authzState) { + idKey := fcm.idKey(authz) + fcm[idKey] = append(fcm[idKey], authz.currentChallenge.Type) +} + +// enqueueUnfailedChallenges enqueues each challenge offered in authz if it +// is not known to have failed for the authz's identifier already. +func (fcm failedChallengeMap) enqueueUnfailedChallenges(authz *authzState) { + idKey := fcm.idKey(authz) + for _, chal := range authz.Challenges { + if !contains(fcm[idKey], chal.Type) { + authz.remainingChallenges = append(authz.remainingChallenges, chal) + } + } +} + +func (fcm failedChallengeMap) idKey(authz *authzState) string { + return authz.Identifier.Type + authz.IdentifierValue() +} + +// challengeTypes is a list of challenges we've seen and/or +// used previously. It sorts from most successful to least +// successful, such that most successful challenges are first. +type challengeTypes []challengeHistory + +// Len is part of sort.Interface. +func (ct challengeTypes) Len() int { return len(ct) } + +// Swap is part of sort.Interface. +func (ct challengeTypes) Swap(i, j int) { ct[i], ct[j] = ct[j], ct[i] } + +// Less is part of sort.Interface. It sorts challenge +// types from highest success ratio to lowest. +func (ct challengeTypes) Less(i, j int) bool { + return ct[i].successRatio() > ct[j].successRatio() +} + +func (ct *challengeTypes) addUnique(challengeType string) { + for _, c := range *ct { + if c.typeName == challengeType { + return + } + } + *ct = append(*ct, challengeHistory{typeName: challengeType}) +} + +func (ct challengeTypes) increment(challengeType string, successful bool) { + defer sort.Stable(ct) // keep most successful challenges in front + for i, c := range ct { + if c.typeName == challengeType { + ct[i].total++ + if successful { + ct[i].successes++ + } + return + } + } +} + +func contains(haystack []string, needle string) bool { + for _, s := range haystack { + if s == needle { + return true + } + } + return false +} + +// retryableErr wraps an error that indicates the caller should retry +// the operation; specifically with a different challenge type. +type retryableErr struct{ error } + +func (re retryableErr) Unwrap() error { return re.error } + +// Keep a list of challenges we've seen offered by servers, +// and prefer keep an ordered list of +var ( + preferredChallenges challengeTypes + preferredChallengesMu sync.Mutex +) diff --git a/vendor/github.com/mholt/acmez/go.mod b/vendor/github.com/mholt/acmez/go.mod new file mode 100644 index 0000000..a0495e5 --- /dev/null +++ b/vendor/github.com/mholt/acmez/go.mod @@ -0,0 +1,8 @@ +module github.com/mholt/acmez + +go 1.14 + +require ( + go.uber.org/zap v1.15.0 + golang.org/x/net v0.0.0-20200707034311-ab3426394381 +) diff --git a/vendor/github.com/mholt/acmez/go.sum b/vendor/github.com/mholt/acmez/go.sum new file mode 100644 index 0000000..929a2dd --- /dev/null +++ b/vendor/github.com/mholt/acmez/go.sum @@ -0,0 +1,61 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/vendor/github.com/mholt/acmez/solver.go b/vendor/github.com/mholt/acmez/solver.go new file mode 100644 index 0000000..8e77b27 --- /dev/null +++ b/vendor/github.com/mholt/acmez/solver.go @@ -0,0 +1,72 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acmez + +import ( + "context" + + "github.com/mholt/acmez/acme" +) + +// Solver is a type that can solve ACME challenges. All +// implementations MUST honor context cancellation. +type Solver interface { + // Present is called just before a challenge is initiated. + // The implementation MUST prepare anything that is necessary + // for completing the challenge; for example, provisioning + // an HTTP resource, TLS certificate, or a DNS record. + // + // It MUST return quickly. If presenting the challenge token + // will take time, then the implementation MUST do the + // minimum amount of work required in this method, and + // SHOULD additionally implement the Waiter interface. + // For example, a DNS challenge solver might make a quick + // HTTP request to a provider's API to create a new DNS + // record, but it might be several minutes or hours before + // the DNS record propagates. The API request should be + // done in Present(), and waiting for propagation should + // be done in Wait(). + Present(context.Context, acme.Challenge) error + + // CleanUp is called after a challenge is finished, whether + // successful or not. It MUST free/remove any resources it + // allocated/created during Present. It SHOULD NOT require + // that Present ran successfully. It MUST return quickly. + CleanUp(context.Context, acme.Challenge) error +} + +// Waiter is an optional interface for Solvers to implement. Its +// primary purpose is to help ensure the challenge can be solved +// before the server gives up trying to verify the challenge. +// +// If implemented, it will be called after Present() but just +// before the challenge is initiated with the server. It blocks +// until the challenge is ready to be solved. (For example, +// waiting on a DNS record to propagate.) This allows challenges +// to succeed that would normally fail because they take too long +// to set up (i.e. the ACME server would give up polling DNS or +// the client would timeout its polling). By separating Present() +// from Wait(), it allows the slow part of all solvers to begin +// up front, rather than waiting on each solver one at a time. +// +// It MUST NOT do anything exclusive of Present() that is required +// for the challenge to succeed. In other words, if Present() is +// called but Wait() is not, then the challenge should still be able +// to succeed assuming infinite time. +// +// Implementations MUST honor context cancellation. +type Waiter interface { + Wait(context.Context, acme.Challenge) error +} diff --git a/vendor/github.com/mholt/acmez/tlsalpn01.go b/vendor/github.com/mholt/acmez/tlsalpn01.go new file mode 100644 index 0000000..a6b920b --- /dev/null +++ b/vendor/github.com/mholt/acmez/tlsalpn01.go @@ -0,0 +1,98 @@ +// Copyright 2020 Matthew Holt +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package acmez + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "math/big" + "time" + + "github.com/mholt/acmez/acme" +) + +// TLSALPN01ChallengeCert creates a certificate that can be used for +// handshakes while solving the tls-alpn-01 challenge. See RFC 8737 §3. +func TLSALPN01ChallengeCert(challenge acme.Challenge) (*tls.Certificate, error) { + keyAuthSum := sha256.Sum256([]byte(challenge.KeyAuthorization)) + keyAuthSumASN1, err := asn1.Marshal(keyAuthSum[:sha256.Size]) + if err != nil { + return nil, err + } + + certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + challengeKeyASN1, err := x509.MarshalECPrivateKey(certKey) + if err != nil { + return nil, err + } + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, err + } + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{CommonName: "ACME challenge"}, + NotBefore: time.Now(), + NotAfter: time.Now().Add(24 * time.Hour * 365), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{challenge.Identifier.Value}, + + // add key authentication digest as the acmeValidation-v1 extension + // (marked as critical such that it won't be used by non-ACME software). + // Reference: https://www.rfc-editor.org/rfc/rfc8737.html#section-3 + ExtraExtensions: []pkix.Extension{ + { + Id: idPEACMEIdentifierV1, + Critical: true, + Value: keyAuthSumASN1, + }, + }, + } + challengeCertDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &certKey.PublicKey, certKey) + if err != nil { + return nil, err + } + + challengeCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: challengeCertDER}) + challengeKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: challengeKeyASN1}) + + cert, err := tls.X509KeyPair(challengeCertPEM, challengeKeyPEM) + if err != nil { + return nil, err + } + + return &cert, nil +} + +// ACMETLS1Protocol is the ALPN value for the TLS-ALPN challenge +// handshake. See RFC 8737 §6.2. +const ACMETLS1Protocol = "acme-tls/1" + +// idPEACMEIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension. +// See RFC 8737 §6.1. https://www.rfc-editor.org/rfc/rfc8737.html#section-6.1 +var idPEACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 126fe62..c324bc0 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -28,6 +28,7 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/coredns/coredns * https://cloudflare.com * https://github.com/abh/geodns +* https://github.com/baidu/bfe * http://www.statdns.com/ * http://www.dnsinspect.com/ * https://github.com/chuangbo/jianbing-dictionary-dns diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go index db2761d..bb8667f 100644 --- a/vendor/github.com/miekg/dns/client.go +++ b/vendor/github.com/miekg/dns/client.go @@ -124,15 +124,38 @@ func (c *Client) Dial(address string) (conn *Conn, err error) { // of 512 bytes // To specify a local address or a timeout, the caller has to set the `Client.Dialer` // attribute appropriately + func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) { + co, err := c.Dial(address) + + if err != nil { + return nil, 0, err + } + defer co.Close() + return c.ExchangeWithConn(m, co) +} + +// ExchangeWithConn has the same behavior as Exchange, just with a predetermined connection +// that will be used instead of creating a new one. +// Usage pattern with a *dns.Client: +// c := new(dns.Client) +// // connection management logic goes here +// +// conn := c.Dial(address) +// in, rtt, err := c.ExchangeWithConn(message, conn) +// +// This allows users of the library to implement their own connection management, +// as opposed to Exchange, which will always use new connections and incur the added overhead +// that entails when using "tcp" and especially "tcp-tls" clients. +func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) { if !c.SingleInflight { - return c.exchange(m, address) + return c.exchange(m, conn) } q := m.Question[0] key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass) r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) { - return c.exchange(m, address) + return c.exchange(m, conn) }) if r != nil && shared { r = r.Copy() @@ -141,15 +164,7 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er return r, rtt, err } -func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - var co *Conn - - co, err = c.Dial(a) - - if err != nil { - return nil, 0, err - } - defer co.Close() +func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) { opt := m.IsEdns0() // If EDNS0 is used use that for size. diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index b059f6f..d874e30 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -105,7 +105,7 @@ func (dns *Msg) SetAxfr(z string) *Msg { // SetTsig appends a TSIG RR to the message. // This is only a skeleton TSIG RR that is added as the last RR in the -// additional section. The Tsig is calculated when the message is being send. +// additional section. The TSIG is calculated when the message is being send. func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg { t := new(TSIG) t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} @@ -317,6 +317,12 @@ func Fqdn(s string) string { return s + "." } +// CanonicalName returns the domain name in canonical form. A name in canonical +// form is lowercase and fully qualified. See Section 6.2 in RFC 4034. +func CanonicalName(s string) string { + return strings.ToLower(Fqdn(s)) +} + // Copied from the official Go code. // ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP @@ -364,7 +370,7 @@ func (t Type) String() string { // String returns the string representation for the class c. func (c Class) String() string { if s, ok := ClassToString[uint16(c)]; ok { - // Only emit mnemonics when they are unambiguous, specically ANY is in both. + // Only emit mnemonics when they are unambiguous, specially ANY is in both. if _, ok := StringToType[s]; !ok { return s } diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go index 12a693f..68c0bd7 100644 --- a/vendor/github.com/miekg/dns/dnssec.go +++ b/vendor/github.com/miekg/dns/dnssec.go @@ -200,7 +200,7 @@ func (k *DNSKEY) ToDS(h uint8) *DS { wire = wire[:n] owner := make([]byte, 255) - off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false) + off, err1 := PackDomainName(CanonicalName(k.Hdr.Name), owner, 0, nil, false) if err1 != nil { return nil } @@ -285,7 +285,7 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error { sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag // For signing, lowercase this name - sigwire.SignerName = strings.ToLower(rr.SignerName) + sigwire.SignerName = CanonicalName(rr.SignerName) // Create the desired binary blob signdata := make([]byte, DefaultMsgSize) @@ -423,7 +423,7 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { sigwire.Expiration = rr.Expiration sigwire.Inception = rr.Inception sigwire.KeyTag = rr.KeyTag - sigwire.SignerName = strings.ToLower(rr.SignerName) + sigwire.SignerName = CanonicalName(rr.SignerName) // Create the desired binary blob signeddata := make([]byte, DefaultMsgSize) n, err := packSigWire(sigwire, signeddata) @@ -659,7 +659,7 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "." } // RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase - h.Name = strings.ToLower(h.Name) + h.Name = CanonicalName(h.Name) // 6.2. Canonical RR Form. (3) - domain rdata to lowercase. // NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, // HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, @@ -672,49 +672,49 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { // conversion. switch x := r1.(type) { case *NS: - x.Ns = strings.ToLower(x.Ns) + x.Ns = CanonicalName(x.Ns) case *MD: - x.Md = strings.ToLower(x.Md) + x.Md = CanonicalName(x.Md) case *MF: - x.Mf = strings.ToLower(x.Mf) + x.Mf = CanonicalName(x.Mf) case *CNAME: - x.Target = strings.ToLower(x.Target) + x.Target = CanonicalName(x.Target) case *SOA: - x.Ns = strings.ToLower(x.Ns) - x.Mbox = strings.ToLower(x.Mbox) + x.Ns = CanonicalName(x.Ns) + x.Mbox = CanonicalName(x.Mbox) case *MB: - x.Mb = strings.ToLower(x.Mb) + x.Mb = CanonicalName(x.Mb) case *MG: - x.Mg = strings.ToLower(x.Mg) + x.Mg = CanonicalName(x.Mg) case *MR: - x.Mr = strings.ToLower(x.Mr) + x.Mr = CanonicalName(x.Mr) case *PTR: - x.Ptr = strings.ToLower(x.Ptr) + x.Ptr = CanonicalName(x.Ptr) case *MINFO: - x.Rmail = strings.ToLower(x.Rmail) - x.Email = strings.ToLower(x.Email) + x.Rmail = CanonicalName(x.Rmail) + x.Email = CanonicalName(x.Email) case *MX: - x.Mx = strings.ToLower(x.Mx) + x.Mx = CanonicalName(x.Mx) case *RP: - x.Mbox = strings.ToLower(x.Mbox) - x.Txt = strings.ToLower(x.Txt) + x.Mbox = CanonicalName(x.Mbox) + x.Txt = CanonicalName(x.Txt) case *AFSDB: - x.Hostname = strings.ToLower(x.Hostname) + x.Hostname = CanonicalName(x.Hostname) case *RT: - x.Host = strings.ToLower(x.Host) + x.Host = CanonicalName(x.Host) case *SIG: - x.SignerName = strings.ToLower(x.SignerName) + x.SignerName = CanonicalName(x.SignerName) case *PX: - x.Map822 = strings.ToLower(x.Map822) - x.Mapx400 = strings.ToLower(x.Mapx400) + x.Map822 = CanonicalName(x.Map822) + x.Mapx400 = CanonicalName(x.Mapx400) case *NAPTR: - x.Replacement = strings.ToLower(x.Replacement) + x.Replacement = CanonicalName(x.Replacement) case *KX: - x.Exchanger = strings.ToLower(x.Exchanger) + x.Exchanger = CanonicalName(x.Exchanger) case *SRV: - x.Target = strings.ToLower(x.Target) + x.Target = CanonicalName(x.Target) case *DNAME: - x.Target = strings.ToLower(x.Target) + x.Target = CanonicalName(x.Target) } // 6.2. Canonical RR Form. (5) - origTTL wire := make([]byte, Len(r1)+1) // +1 to be safe(r) diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index 3318b77..9242168 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -209,7 +209,7 @@ Basic use pattern validating and replying to a message that has TSIG set. // *Msg r has an TSIG record and it was validated m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) } else { - // *Msg r has an TSIG records and it was not valided + // *Msg r has an TSIG records and it was not validated } } w.WriteMsg(m) diff --git a/vendor/github.com/miekg/dns/duplicate.go b/vendor/github.com/miekg/dns/duplicate.go index 49e6940..d21ae1c 100644 --- a/vendor/github.com/miekg/dns/duplicate.go +++ b/vendor/github.com/miekg/dns/duplicate.go @@ -3,9 +3,8 @@ package dns //go:generate go run duplicate_generate.go // IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL. -// So this means the header data is equal *and* the RDATA is the same. Return true -// is so, otherwise false. -// It's a protocol violation to have identical RRs in a message. +// So this means the header data is equal *and* the RDATA is the same. Returns true +// if so, otherwise false. It's a protocol violation to have identical RRs in a message. func IsDuplicate(r1, r2 RR) bool { // Check whether the record header is identical. if !r1.Header().isDuplicate(r2.Header()) { diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go index f7e91a2..f713074 100644 --- a/vendor/github.com/miekg/dns/generate.go +++ b/vendor/github.com/miekg/dns/generate.go @@ -20,13 +20,13 @@ import ( // of $ after that are interpreted. func (zp *ZoneParser) generate(l lex) (RR, bool) { token := l.token - step := 1 + step := int64(1) if i := strings.IndexByte(token, '/'); i >= 0 { if i+1 == len(token) { return zp.setParseError("bad step in $GENERATE range", l) } - s, err := strconv.Atoi(token[i+1:]) + s, err := strconv.ParseInt(token[i+1:], 10, 64) if err != nil || s <= 0 { return zp.setParseError("bad step in $GENERATE range", l) } @@ -40,12 +40,12 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { return zp.setParseError("bad start-stop in $GENERATE range", l) } - start, err := strconv.Atoi(sx[0]) + start, err := strconv.ParseInt(sx[0], 10, 64) if err != nil { return zp.setParseError("bad start in $GENERATE range", l) } - end, err := strconv.Atoi(sx[1]) + end, err := strconv.ParseInt(sx[1], 10, 64) if err != nil { return zp.setParseError("bad stop in $GENERATE range", l) } @@ -75,10 +75,10 @@ func (zp *ZoneParser) generate(l lex) (RR, bool) { r := &generateReader{ s: s, - cur: start, - start: start, - end: end, - step: step, + cur: int(start), + start: int(start), + end: int(end), + step: int(step), file: zp.file, lex: &l, @@ -188,7 +188,7 @@ func (r *generateReader) ReadByte() (byte, error) { if errMsg != "" { return 0, r.parseError(errMsg, si+3+sep) } - if r.start+offset < 0 || r.end+offset > 1<<31-1 { + if r.start+offset < 0 || int64(r.end) + int64(offset) > 1<<31-1 { return 0, r.parseError("bad offset in $GENERATE", si+3+sep) } @@ -229,19 +229,19 @@ func modToPrintf(s string) (string, int, string) { return "", 0, "bad base in $GENERATE" } - offset, err := strconv.Atoi(offStr) + offset, err := strconv.ParseInt(offStr, 10, 64) if err != nil { return "", 0, "bad offset in $GENERATE" } - width, err := strconv.Atoi(widthStr) + width, err := strconv.ParseInt(widthStr, 10, 64) if err != nil || width < 0 || width > 255 { return "", 0, "bad width in $GENERATE" } if width == 0 { - return "%" + base, offset, "" + return "%" + base, int(offset), "" } - return "%0" + widthStr + base, offset, "" + return "%0" + widthStr + base, int(offset), "" } diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index 10d8247..df1675d 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -83,7 +83,7 @@ func CompareDomainName(s1, s2 string) (n int) { return } -// CountLabel counts the the number of labels in the string s. +// CountLabel counts the number of labels in the string s. // s must be a syntactically valid domain name. func CountLabel(s string) (labels int) { if s == "." { diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 2938130..7001f6d 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -398,17 +398,12 @@ Loop: return "", lenmsg, ErrLongDomain } for _, b := range msg[off : off+c] { - switch b { - case '.', '(', ')', ';', ' ', '@': - fallthrough - case '"', '\\': + if isDomainNameLabelSpecial(b) { s = append(s, '\\', b) - default: - if b < ' ' || b > '~' { // unprintable, use \DDD - s = append(s, escapeByte(b)...) - } else { - s = append(s, b) - } + } else if b < ' ' || b > '~' { + s = append(s, escapeByte(b)...) + } else { + s = append(s, b) } } s = append(s, '.') @@ -661,7 +656,6 @@ func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) } // If offset does not increase anymore, l is a lie if off1 == off { - l = i break } dst = append(dst, r) diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go index 98fadc3..cbcab57 100644 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ b/vendor/github.com/miekg/dns/msg_helpers.go @@ -423,86 +423,12 @@ Option: if off+int(optlen) > len(msg) { return nil, len(msg), &Error{err: "overflow unpacking opt"} } - switch code { - case EDNS0NSID: - e := new(EDNS0_NSID) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0SUBNET: - e := new(EDNS0_SUBNET) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0COOKIE: - e := new(EDNS0_COOKIE) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0EXPIRE: - e := new(EDNS0_EXPIRE) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0UL: - e := new(EDNS0_UL) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0LLQ: - e := new(EDNS0_LLQ) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0DAU: - e := new(EDNS0_DAU) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0DHU: - e := new(EDNS0_DHU) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0N3U: - e := new(EDNS0_N3U) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0PADDING: - e := new(EDNS0_PADDING) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - default: - e := new(EDNS0_LOCAL) - e.Code = code - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) + e := makeDataOpt(code) + if err := e.unpack(msg[off : off+int(optlen)]); err != nil { + return nil, len(msg), err } + edns = append(edns, e) + off += int(optlen) if off < len(msg) { goto Option @@ -511,6 +437,35 @@ Option: return edns, off, nil } +func makeDataOpt(code uint16) EDNS0 { + switch code { + case EDNS0NSID: + return new(EDNS0_NSID) + case EDNS0SUBNET: + return new(EDNS0_SUBNET) + case EDNS0COOKIE: + return new(EDNS0_COOKIE) + case EDNS0EXPIRE: + return new(EDNS0_EXPIRE) + case EDNS0UL: + return new(EDNS0_UL) + case EDNS0LLQ: + return new(EDNS0_LLQ) + case EDNS0DAU: + return new(EDNS0_DAU) + case EDNS0DHU: + return new(EDNS0_DHU) + case EDNS0N3U: + return new(EDNS0_N3U) + case EDNS0PADDING: + return new(EDNS0_PADDING) + default: + e := new(EDNS0_LOCAL) + e.Code = code + return e + } +} + func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) { for _, el := range options { b, err := el.pack() @@ -521,9 +476,7 @@ func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) { binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length off += 4 if off+len(b) > len(msg) { - copy(msg[off:], b) - off = len(msg) - continue + return len(msg), &Error{err: "overflow packing opt"} } // Actual data copy(msg[off:off+len(b)], b) @@ -783,28 +736,31 @@ func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) { if int(prefix) > 8*len(ip) { return APLPrefix{}, len(msg), &Error{err: "APL prefix too long"} } - afdlen := int(nlen & 0x7f) - if (int(prefix)+7)/8 != afdlen { - return APLPrefix{}, len(msg), &Error{err: "invalid APL address length"} + if afdlen > len(ip) { + return APLPrefix{}, len(msg), &Error{err: "APL length too long"} } if off+afdlen > len(msg) { return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL address"} } off += copy(ip, msg[off:off+afdlen]) - if prefix%8 > 0 { + if afdlen > 0 { last := ip[afdlen-1] - zero := uint8(0xff) >> (prefix % 8) - if last&zero > 0 { + if last == 0 { return APLPrefix{}, len(msg), &Error{err: "extra APL address bits"} } } + ipnet := net.IPNet{ + IP: ip, + Mask: net.CIDRMask(int(prefix), 8*len(ip)), + } + network := ipnet.IP.Mask(ipnet.Mask) + if !network.Equal(ipnet.IP) { + return APLPrefix{}, len(msg), &Error{err: "invalid APL address length"} + } return APLPrefix{ Negation: (nlen & 0x80) != 0, - Network: net.IPNet{ - IP: ip, - Mask: net.CIDRMask(int(prefix), 8*len(ip)), - }, + Network: ipnet, }, off, nil } diff --git a/vendor/github.com/miekg/dns/msg_truncate.go b/vendor/github.com/miekg/dns/msg_truncate.go index 89d4075..a76150a 100644 --- a/vendor/github.com/miekg/dns/msg_truncate.go +++ b/vendor/github.com/miekg/dns/msg_truncate.go @@ -73,7 +73,7 @@ func (dns *Msg) Truncate(size int) { var numExtra int if l < size { - l, numExtra = truncateLoop(dns.Extra, size, l, compression) + _, numExtra = truncateLoop(dns.Extra, size, l, compression) } // See the function documentation for when we set this. diff --git a/vendor/github.com/miekg/dns/nsecx.go b/vendor/github.com/miekg/dns/nsecx.go index 8f071a4..f882681 100644 --- a/vendor/github.com/miekg/dns/nsecx.go +++ b/vendor/github.com/miekg/dns/nsecx.go @@ -43,7 +43,7 @@ func HashName(label string, ha uint8, iter uint16, salt string) string { return toBase32(nsec3) } -// Cover returns true if a name is covered by the NSEC3 record +// Cover returns true if a name is covered by the NSEC3 record. func (rr *NSEC3) Cover(name string) bool { nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt) owner := strings.ToUpper(rr.Hdr.Name) diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go index e28f066..cda6cae 100644 --- a/vendor/github.com/miekg/dns/privaterr.go +++ b/vendor/github.com/miekg/dns/privaterr.go @@ -13,7 +13,6 @@ type PrivateRdata interface { // Pack is used when packing a private RR into a buffer. Pack([]byte) (int, error) // Unpack is used when unpacking a private RR from a buffer. - // TODO(miek): diff. signature than Pack, see edns0.go for instance. Unpack([]byte) (int, error) // Copy copies the Rdata into the PrivateRdata argument. Copy(PrivateRdata) error diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go index 671018b..e18566f 100644 --- a/vendor/github.com/miekg/dns/scan.go +++ b/vendor/github.com/miekg/dns/scan.go @@ -87,31 +87,18 @@ type lex struct { column int // column in the file } -// Token holds the token that are returned when a zone file is parsed. -type Token struct { - // The scanned resource record when error is not nil. - RR - // When an error occurred, this has the error specifics. - Error *ParseError - // A potential comment positioned after the RR and on the same line. - Comment string -} - // ttlState describes the state necessary to fill in an omitted RR TTL type ttlState struct { ttl uint32 // ttl is the current default TTL isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive } -// NewRR reads the RR contained in the string s. Only the first RR is -// returned. If s contains no records, NewRR will return nil with no -// error. +// NewRR reads the RR contained in the string s. Only the first RR is returned. +// If s contains no records, NewRR will return nil with no error. // -// The class defaults to IN and TTL defaults to 3600. The full zone -// file syntax like $TTL, $ORIGIN, etc. is supported. -// -// All fields of the returned RR are set, except RR.Header().Rdlength -// which is set to 0. +// The class defaults to IN and TTL defaults to 3600. The full zone file syntax +// like $TTL, $ORIGIN, etc. is supported. All fields of the returned RR are +// set, except RR.Header().Rdlength which is set to 0. func NewRR(s string) (RR, error) { if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline return ReadRR(strings.NewReader(s+"\n"), "") @@ -133,70 +120,6 @@ func ReadRR(r io.Reader, file string) (RR, error) { return rr, zp.Err() } -// ParseZone reads a RFC 1035 style zonefile from r. It returns -// Tokens on the returned channel, each consisting of either a -// parsed RR and optional comment or a nil RR and an error. The -// channel is closed by ParseZone when the end of r is reached. -// -// The string file is used in error reporting and to resolve relative -// $INCLUDE directives. The string origin is used as the initial -// origin, as if the file would start with an $ORIGIN directive. -// -// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all -// supported. Note that $GENERATE's range support up to a maximum of -// of 65535 steps. -// -// Basic usage pattern when reading from a string (z) containing the -// zone data: -// -// for x := range dns.ParseZone(strings.NewReader(z), "", "") { -// if x.Error != nil { -// // log.Println(x.Error) -// } else { -// // Do something with x.RR -// } -// } -// -// Comments specified after an RR (and on the same line!) are -// returned too: -// -// foo. IN A 10.0.0.1 ; this is a comment -// -// The text "; this is comment" is returned in Token.Comment. -// Comments inside the RR are returned concatenated along with the -// RR. Comments on a line by themselves are discarded. -// -// To prevent memory leaks it is important to always fully drain the -// returned channel. If an error occurs, it will always be the last -// Token sent on the channel. -// -// Deprecated: New users should prefer the ZoneParser API. -func ParseZone(r io.Reader, origin, file string) chan *Token { - t := make(chan *Token, 10000) - go parseZone(r, origin, file, t) - return t -} - -func parseZone(r io.Reader, origin, file string, t chan *Token) { - defer close(t) - - zp := NewZoneParser(r, origin, file) - zp.SetIncludeAllowed(true) - - for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { - t <- &Token{RR: rr, Comment: zp.Comment()} - } - - if err := zp.Err(); err != nil { - pe, ok := err.(*ParseError) - if !ok { - pe = &ParseError{file: file, err: err.Error()} - } - - t <- &Token{Error: pe} - } -} - // ZoneParser is a parser for an RFC 1035 style zonefile. // // Each parsed RR in the zone is returned sequentially from Next. An @@ -247,7 +170,7 @@ type ZoneParser struct { includeDepth uint8 - includeAllowed bool + includeAllowed bool generateDisallowed bool } diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go index 6c37b2e..11b08ad 100644 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ b/vendor/github.com/miekg/dns/scan_rr.go @@ -1,6 +1,7 @@ package dns import ( + "bytes" "encoding/base64" "net" "strconv" @@ -10,15 +11,15 @@ import ( // A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) // or an error func endingToString(c *zlexer, errstr string) (string, *ParseError) { - var s string + var buffer bytes.Buffer l, _ := c.Next() // zString for l.value != zNewline && l.value != zEOF { if l.err { - return s, &ParseError{"", errstr, l} + return buffer.String(), &ParseError{"", errstr, l} } switch l.value { case zString: - s += l.token + buffer.WriteString(l.token) case zBlank: // Ok default: return "", &ParseError{"", errstr, l} @@ -26,7 +27,7 @@ func endingToString(c *zlexer, errstr string) (string, *ParseError) { l, _ = c.Next() } - return s, nil + return buffer.String(), nil } // A remainder of the rdata with embedded spaces, split on unquoted whitespace @@ -403,7 +404,7 @@ func (rr *SOA) parse(c *zlexer, o string) *ParseError { if l.err { return &ParseError{"", "bad SOA zone parameter", l} } - if j, e := strconv.ParseUint(l.token, 10, 32); e != nil { + if j, err := strconv.ParseUint(l.token, 10, 32); err != nil { if i == 0 { // Serial must be a number return &ParseError{"", "bad SOA zone parameter", l} @@ -446,16 +447,16 @@ func (rr *SRV) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil || l.err { return &ParseError{"", "bad SRV Weight", l} } rr.Weight = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 16) + if e2 != nil || l.err { return &ParseError{"", "bad SRV Port", l} } rr.Port = uint16(i) @@ -482,8 +483,8 @@ func (rr *NAPTR) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil || l.err { return &ParseError{"", "bad NAPTR Preference", l} } rr.Preference = uint16(i) @@ -581,9 +582,9 @@ func (rr *TALINK) parse(c *zlexer, o string) *ParseError { func (rr *LOC) parse(c *zlexer, o string) *ParseError { // Non zero defaults for LOC record, see RFC 1876, Section 3. - rr.HorizPre = 165 // 10000 - rr.VertPre = 162 // 10 - rr.Size = 18 // 1 + rr.Size = 0x12 // 1e2 cm (1m) + rr.HorizPre = 0x16 // 1e6 cm (10000m) + rr.VertPre = 0x13 // 1e3 cm (10m) ok := false // North @@ -600,15 +601,15 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError { if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { goto East } - i, e = strconv.ParseUint(l.token, 10, 32) - if e != nil || l.err { + if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err { return &ParseError{"", "bad LOC Latitude minutes", l} + } else { + rr.Latitude += 1000 * 60 * uint32(i) } - rr.Latitude += 1000 * 60 * uint32(i) c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { + if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err { return &ParseError{"", "bad LOC Latitude seconds", l} } else { rr.Latitude += uint32(1000 * i) @@ -626,7 +627,7 @@ East: // East c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err { + if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err { return &ParseError{"", "bad LOC Longitude", l} } else { rr.Longitude = 1000 * 60 * 60 * uint32(i) @@ -637,14 +638,14 @@ East: if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { goto Altitude } - if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err { + if i, err := strconv.ParseUint(l.token, 10, 32); err != nil || l.err { return &ParseError{"", "bad LOC Longitude minutes", l} } else { rr.Longitude += 1000 * 60 * uint32(i) } c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { + if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err { return &ParseError{"", "bad LOC Longitude seconds", l} } else { rr.Longitude += uint32(1000 * i) @@ -667,7 +668,7 @@ Altitude: if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { l.token = l.token[0 : len(l.token)-1] } - if i, e := strconv.ParseFloat(l.token, 32); e != nil { + if i, err := strconv.ParseFloat(l.token, 32); err != nil { return &ParseError{"", "bad LOC Altitude", l} } else { rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5) @@ -681,23 +682,23 @@ Altitude: case zString: switch count { case 0: // Size - e, m, ok := stringToCm(l.token) + exp, m, ok := stringToCm(l.token) if !ok { return &ParseError{"", "bad LOC Size", l} } - rr.Size = e&0x0f | m<<4&0xf0 + rr.Size = exp&0x0f | m<<4&0xf0 case 1: // HorizPre - e, m, ok := stringToCm(l.token) + exp, m, ok := stringToCm(l.token) if !ok { return &ParseError{"", "bad LOC HorizPre", l} } - rr.HorizPre = e&0x0f | m<<4&0xf0 + rr.HorizPre = exp&0x0f | m<<4&0xf0 case 2: // VertPre - e, m, ok := stringToCm(l.token) + exp, m, ok := stringToCm(l.token) if !ok { return &ParseError{"", "bad LOC VertPre", l} } - rr.VertPre = e&0x0f | m<<4&0xf0 + rr.VertPre = exp&0x0f | m<<4&0xf0 } count++ case zBlank: @@ -762,7 +763,7 @@ func (rr *CERT) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() if v, ok := StringToCertType[l.token]; ok { rr.Type = v - } else if i, e := strconv.ParseUint(l.token, 10, 16); e != nil { + } else if i, err := strconv.ParseUint(l.token, 10, 16); err != nil { return &ParseError{"", "bad CERT Type", l} } else { rr.Type = uint16(i) @@ -778,7 +779,7 @@ func (rr *CERT) parse(c *zlexer, o string) *ParseError { l, _ = c.Next() // zString if v, ok := StringToAlgorithm[l.token]; ok { rr.Algorithm = v - } else if i, e := strconv.ParseUint(l.token, 10, 8); e != nil { + } else if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { return &ParseError{"", "bad CERT Algorithm", l} } else { rr.Algorithm = uint8(i) @@ -812,8 +813,8 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() - j, e = strconv.ParseUint(l.token, 10, 16) - if e != nil { + j, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil { // Serial must be a number return &ParseError{"", "bad CSYNC flags", l} } @@ -845,9 +846,7 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { return nil } -func (rr *SIG) parse(c *zlexer, o string) *ParseError { - return rr.RRSIG.parse(c, o) -} +func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) } func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() @@ -868,24 +867,24 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { + i, e := strconv.ParseUint(l.token, 10, 8) + if e != nil || l.err { return &ParseError{"", "bad RRSIG Algorithm", l} } rr.Algorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad RRSIG Labels", l} } rr.Labels = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 32) - if err != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 32) + if e2 != nil || l.err { return &ParseError{"", "bad RRSIG OrigTtl", l} } rr.OrigTtl = uint32(i) @@ -918,8 +917,8 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { c.Next() // zBlank l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 16) - if err != nil || l.err { + i, e3 := strconv.ParseUint(l.token, 10, 16) + if e3 != nil || l.err { return &ParseError{"", "bad RRSIG KeyTag", l} } rr.KeyTag = uint16(i) @@ -933,9 +932,9 @@ func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { } rr.SignerName = name - s, e := endingToString(c, "bad RRSIG Signature") - if e != nil { - return e + s, e4 := endingToString(c, "bad RRSIG Signature") + if e4 != nil { + return e4 } rr.Signature = s @@ -985,15 +984,15 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad NSEC3 Flags", l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 16) + if e2 != nil || l.err { return &ParseError{"", "bad NSEC3 Iterations", l} } rr.Iterations = uint16(i) @@ -1050,22 +1049,22 @@ func (rr *NSEC3PARAM) parse(c *zlexer, o string) *ParseError { rr.Hash = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad NSEC3PARAM Flags", l} } rr.Flags = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 16) + if e2 != nil || l.err { return &ParseError{"", "bad NSEC3PARAM Iterations", l} } rr.Iterations = uint16(i) c.Next() l, _ = c.Next() if l.token != "-" { - rr.SaltLength = uint8(len(l.token)) + rr.SaltLength = uint8(len(l.token) / 2) rr.Salt = l.token } return slurpRemainder(c) @@ -1132,15 +1131,15 @@ func (rr *SSHFP) parse(c *zlexer, o string) *ParseError { rr.Algorithm = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad SSHFP Type", l} } rr.Type = uint8(i) c.Next() // zBlank - s, e1 := endingToString(c, "bad SSHFP Fingerprint") - if e1 != nil { - return e1 + s, e2 := endingToString(c, "bad SSHFP Fingerprint") + if e2 != nil { + return e2 } rr.FingerPrint = s return nil @@ -1155,37 +1154,32 @@ func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, typ string) *ParseError { rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad " + typ + " Protocol", l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { return &ParseError{"", "bad " + typ + " Algorithm", l} } rr.Algorithm = uint8(i) - s, e1 := endingToString(c, "bad "+typ+" PublicKey") - if e1 != nil { - return e1 + s, e3 := endingToString(c, "bad "+typ+" PublicKey") + if e3 != nil { + return e3 } rr.PublicKey = s return nil } -func (rr *DNSKEY) parse(c *zlexer, o string) *ParseError { - return rr.parseDNSKEY(c, o, "DNSKEY") -} - -func (rr *KEY) parse(c *zlexer, o string) *ParseError { - return rr.parseDNSKEY(c, o, "KEY") -} - -func (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError { - return rr.parseDNSKEY(c, o, "CDNSKEY") -} +func (rr *DNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "DNSKEY") } +func (rr *KEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "KEY") } +func (rr *CDNSKEY) parse(c *zlexer, o string) *ParseError { return rr.parseDNSKEY(c, o, "CDNSKEY") } +func (rr *DS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DS") } +func (rr *DLV) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "DLV") } +func (rr *CDS) parse(c *zlexer, o string) *ParseError { return rr.parseDS(c, o, "CDS") } func (rr *RKEY) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() @@ -1196,21 +1190,21 @@ func (rr *RKEY) parse(c *zlexer, o string) *ParseError { rr.Flags = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad RKEY Protocol", l} } rr.Protocol = uint8(i) c.Next() // zBlank l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { return &ParseError{"", "bad RKEY Algorithm", l} } rr.Algorithm = uint8(i) - s, e1 := endingToString(c, "bad RKEY PublicKey") - if e1 != nil { - return e1 + s, e3 := endingToString(c, "bad RKEY PublicKey") + if e3 != nil { + return e3 } rr.PublicKey = s return nil @@ -1243,15 +1237,15 @@ func (rr *GPOS) parse(c *zlexer, o string) *ParseError { rr.Longitude = l.token c.Next() // zBlank l, _ = c.Next() - _, e = strconv.ParseFloat(l.token, 64) - if e != nil || l.err { + _, e1 := strconv.ParseFloat(l.token, 64) + if e1 != nil || l.err { return &ParseError{"", "bad GPOS Latitude", l} } rr.Latitude = l.token c.Next() // zBlank l, _ = c.Next() - _, e = strconv.ParseFloat(l.token, 64) - if e != nil || l.err { + _, e2 := strconv.ParseFloat(l.token, 64) + if e2 != nil || l.err { return &ParseError{"", "bad GPOS Altitude", l} } rr.Altitude = l.token @@ -1267,7 +1261,7 @@ func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError { rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() - if i, e = strconv.ParseUint(l.token, 10, 8); e != nil { + if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { @@ -1279,31 +1273,19 @@ func (rr *DS) parseDS(c *zlexer, o, typ string) *ParseError { } c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad " + typ + " DigestType", l} } rr.DigestType = uint8(i) - s, e1 := endingToString(c, "bad "+typ+" Digest") - if e1 != nil { - return e1 + s, e2 := endingToString(c, "bad "+typ+" Digest") + if e2 != nil { + return e2 } rr.Digest = s return nil } -func (rr *DS) parse(c *zlexer, o string) *ParseError { - return rr.parseDS(c, o, "DS") -} - -func (rr *DLV) parse(c *zlexer, o string) *ParseError { - return rr.parseDS(c, o, "DLV") -} - -func (rr *CDS) parse(c *zlexer, o string) *ParseError { - return rr.parseDS(c, o, "CDS") -} - func (rr *TA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() i, e := strconv.ParseUint(l.token, 10, 16) @@ -1313,7 +1295,7 @@ func (rr *TA) parse(c *zlexer, o string) *ParseError { rr.KeyTag = uint16(i) c.Next() // zBlank l, _ = c.Next() - if i, e := strconv.ParseUint(l.token, 10, 8); e != nil { + if i, err := strconv.ParseUint(l.token, 10, 8); err != nil { tokenUpper := strings.ToUpper(l.token) i, ok := StringToAlgorithm[tokenUpper] if !ok || l.err { @@ -1325,14 +1307,14 @@ func (rr *TA) parse(c *zlexer, o string) *ParseError { } c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad TA DigestType", l} } rr.DigestType = uint8(i) - s, err := endingToString(c, "bad TA Digest") - if err != nil { - return err + s, e2 := endingToString(c, "bad TA Digest") + if e2 != nil { + return e2 } rr.Digest = s return nil @@ -1347,22 +1329,22 @@ func (rr *TLSA) parse(c *zlexer, o string) *ParseError { rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad TLSA Selector", l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { return &ParseError{"", "bad TLSA MatchingType", l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t - s, e2 := endingToString(c, "bad TLSA Certificate") - if e2 != nil { - return e2 + s, e3 := endingToString(c, "bad TLSA Certificate") + if e3 != nil { + return e3 } rr.Certificate = s return nil @@ -1377,22 +1359,22 @@ func (rr *SMIMEA) parse(c *zlexer, o string) *ParseError { rr.Usage = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad SMIMEA Selector", l} } rr.Selector = uint8(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { + i, e2 := strconv.ParseUint(l.token, 10, 8) + if e2 != nil || l.err { return &ParseError{"", "bad SMIMEA MatchingType", l} } rr.MatchingType = uint8(i) // So this needs be e2 (i.e. different than e), because...??t - s, e2 := endingToString(c, "bad SMIMEA Certificate") - if e2 != nil { - return e2 + s, e3 := endingToString(c, "bad SMIMEA Certificate") + if e3 != nil { + return e3 } rr.Certificate = s return nil @@ -1469,16 +1451,16 @@ func (rr *URI) parse(c *zlexer, o string) *ParseError { rr.Priority = uint16(i) c.Next() // zBlank l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 16) + if e1 != nil || l.err { return &ParseError{"", "bad URI Weight", l} } rr.Weight = uint16(i) c.Next() // zBlank - s, err := endingToTxtSlice(c, "bad URI Target") - if err != nil { - return err + s, e2 := endingToTxtSlice(c, "bad URI Target") + if e2 != nil { + return e2 } if len(s) != 1 { return &ParseError{"", "bad URI Target", l} @@ -1506,9 +1488,9 @@ func (rr *NID) parse(c *zlexer, o string) *ParseError { rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - u, err := stringToNodeID(l) - if err != nil || l.err { - return err + u, e1 := stringToNodeID(l) + if e1 != nil || l.err { + return e1 } rr.NodeID = u return slurpRemainder(c) @@ -1546,7 +1528,6 @@ func (rr *LP) parse(c *zlexer, o string) *ParseError { return &ParseError{"", "bad LP Fqdn", l} } rr.Fqdn = name - return slurpRemainder(c) } @@ -1559,9 +1540,9 @@ func (rr *L64) parse(c *zlexer, o string) *ParseError { rr.Preference = uint16(i) c.Next() // zBlank l, _ = c.Next() // zString - u, err := stringToNodeID(l) - if err != nil || l.err { - return err + u, e1 := stringToNodeID(l) + if e1 != nil || l.err { + return e1 } rr.Locator64 = u return slurpRemainder(c) @@ -1624,14 +1605,13 @@ func (rr *PX) parse(c *zlexer, o string) *ParseError { return &ParseError{"", "bad PX Mapx400", l} } rr.Mapx400 = mapx400 - return slurpRemainder(c) } func (rr *CAA) parse(c *zlexer, o string) *ParseError { l, _ := c.Next() - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { + i, e := strconv.ParseUint(l.token, 10, 8) + if e != nil || l.err { return &ParseError{"", "bad CAA Flag", l} } rr.Flag = uint8(i) @@ -1644,9 +1624,9 @@ func (rr *CAA) parse(c *zlexer, o string) *ParseError { rr.Tag = l.token c.Next() // zBlank - s, e := endingToTxtSlice(c, "bad CAA Value") - if e != nil { - return e + s, e1 := endingToTxtSlice(c, "bad CAA Value") + if e1 != nil { + return e1 } if len(s) != 1 { return &ParseError{"", "bad CAA Value", l} @@ -1667,8 +1647,8 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError { // Get the key length and key values l, _ = c.Next() - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { + i, e := strconv.ParseUint(l.token, 10, 8) + if e != nil || l.err { return &ParseError{"", "bad TKEY key length", l} } rr.KeySize = uint16(i) @@ -1682,8 +1662,8 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError { // Get the otherdata length and string data l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { + i, e1 := strconv.ParseUint(l.token, 10, 8) + if e1 != nil || l.err { return &ParseError{"", "bad TKEY otherdata length", l} } rr.OtherLen = uint16(i) @@ -1693,7 +1673,6 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError { return &ParseError{"", "bad TKEY otherday", l} } rr.OtherData = l.token - return nil } @@ -1727,9 +1706,9 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError { family = family[1:] } - afi, err := strconv.ParseUint(family, 10, 16) - if err != nil { - return &ParseError{"", "failed to parse APL family: " + err.Error(), l} + afi, e := strconv.ParseUint(family, 10, 16) + if e != nil { + return &ParseError{"", "failed to parse APL family: " + e.Error(), l} } var addrLen int switch afi { @@ -1741,9 +1720,9 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError { return &ParseError{"", "unrecognized APL family", l} } - ip, subnet, err := net.ParseCIDR(cidr) - if err != nil { - return &ParseError{"", "failed to parse APL address: " + err.Error(), l} + ip, subnet, e1 := net.ParseCIDR(cidr) + if e1 != nil { + return &ParseError{"", "failed to parse APL address: " + e1.Error(), l} } if !ip.Equal(subnet.IP) { return &ParseError{"", "extra bits in APL address", l} diff --git a/vendor/github.com/miekg/dns/serve_mux.go b/vendor/github.com/miekg/dns/serve_mux.go index 69deb33..aadb0bf 100644 --- a/vendor/github.com/miekg/dns/serve_mux.go +++ b/vendor/github.com/miekg/dns/serve_mux.go @@ -1,7 +1,6 @@ package dns import ( - "strings" "sync" ) @@ -36,7 +35,7 @@ func (mux *ServeMux) match(q string, t uint16) Handler { return nil } - q = strings.ToLower(q) + q = CanonicalName(q) var handler Handler for off, end := 0, false; !end; off, end = NextLabel(q, off) { @@ -66,7 +65,7 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { if mux.z == nil { mux.z = make(map[string]Handler) } - mux.z[Fqdn(pattern)] = handler + mux.z[CanonicalName(pattern)] = handler mux.m.Unlock() } @@ -81,7 +80,7 @@ func (mux *ServeMux) HandleRemove(pattern string) { panic("dns: invalid pattern " + pattern) } mux.m.Lock() - delete(mux.z, Fqdn(pattern)) + delete(mux.z, CanonicalName(pattern)) mux.m.Unlock() } diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go index 61efa24..9451f1a 100644 --- a/vendor/github.com/miekg/dns/tsig.go +++ b/vendor/github.com/miekg/dns/tsig.go @@ -115,7 +115,7 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s t := new(TSIG) var h hash.Hash - switch strings.ToLower(rr.Algorithm) { + switch CanonicalName(rr.Algorithm) { case HmacMD5: h = hmac.New(md5.New, rawsecret) case HmacSHA1: @@ -182,7 +182,7 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { } var h hash.Hash - switch strings.ToLower(tsig.Algorithm) { + switch CanonicalName(tsig.Algorithm) { case HmacMD5: h = hmac.New(md5.New, rawsecret) case HmacSHA1: @@ -232,10 +232,10 @@ func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []b tsigvar = tsigvar[:n] } else { tsig := new(tsigWireFmt) - tsig.Name = strings.ToLower(rr.Hdr.Name) + tsig.Name = CanonicalName(rr.Hdr.Name) tsig.Class = ClassANY tsig.Ttl = rr.Hdr.Ttl - tsig.Algorithm = strings.ToLower(rr.Algorithm) + tsig.Algorithm = CanonicalName(rr.Algorithm) tsig.TimeSigned = rr.TimeSigned tsig.Fudge = rr.Fudge tsig.Error = rr.Error diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index a6048cb..7776b4f 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -165,11 +165,11 @@ const ( _RD = 1 << 8 // recursion desired _RA = 1 << 7 // recursion available _Z = 1 << 6 // Z - _AD = 1 << 5 // authticated data + _AD = 1 << 5 // authenticated data _CD = 1 << 4 // checking disabled ) -// Various constants used in the LOC RR, See RFC 1887. +// Various constants used in the LOC RR. See RFC 1887. const ( LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. @@ -209,8 +209,11 @@ var CertTypeToString = map[uint16]string{ //go:generate go run types_generate.go -// Question holds a DNS question. There can be multiple questions in the -// question section of a message. Usually there is just one. +// Question holds a DNS question. Usually there is just one. While the +// original DNS RFCs allow multiple questions in the question section of a +// message, in practice it never works. Because most DNS servers see multiple +// questions as an error, it is recommended to only have one question per +// message. type Question struct { Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed) Qtype uint16 @@ -231,7 +234,7 @@ func (q *Question) String() (s string) { return s } -// ANY is a wildcard record. See RFC 1035, Section 3.2.3. ANY +// ANY is a wild card record. See RFC 1035, Section 3.2.3. ANY // is named "*" there. type ANY struct { Hdr RR_Header @@ -442,45 +445,38 @@ func sprintName(s string) string { var dst strings.Builder for i := 0; i < len(s); { - if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' { + if s[i] == '.' { if dst.Len() != 0 { - dst.WriteString(s[i : i+2]) + dst.WriteByte('.') } - i += 2 + i++ continue } b, n := nextByte(s, i) if n == 0 { - i++ - continue - } - if b == '.' { - if dst.Len() != 0 { - dst.WriteByte('.') + // Drop "dangling" incomplete escapes. + if dst.Len() == 0 { + return s[:i] } - i += n - continue + break } - switch b { - case ' ', '\'', '@', ';', '(', ')', '"', '\\': // additional chars to escape + if isDomainNameLabelSpecial(b) { if dst.Len() == 0 { dst.Grow(len(s) * 2) dst.WriteString(s[:i]) } dst.WriteByte('\\') dst.WriteByte(b) - default: - if ' ' <= b && b <= '~' { - if dst.Len() != 0 { - dst.WriteByte(b) - } - } else { - if dst.Len() == 0 { - dst.Grow(len(s) * 2) - dst.WriteString(s[:i]) - } - dst.WriteString(escapeByte(b)) + } else if b < ' ' || b > '~' { // unprintable, use \DDD + if dst.Len() == 0 { + dst.Grow(len(s) * 2) + dst.WriteString(s[:i]) + } + dst.WriteString(escapeByte(b)) + } else { + if dst.Len() != 0 { + dst.WriteByte(b) } } i += n @@ -503,15 +499,10 @@ func sprintTxtOctet(s string) string { } b, n := nextByte(s, i) - switch { - case n == 0: + if n == 0 { i++ // dangling back slash - case b == '.': - dst.WriteByte('.') - case b < ' ' || b > '~': - dst.WriteString(escapeByte(b)) - default: - dst.WriteByte(b) + } else { + writeTXTStringByte(&dst, b) } i += n } @@ -587,6 +578,17 @@ func escapeByte(b byte) string { return escapedByteLarge[int(b)*4 : int(b)*4+4] } +// isDomainNameLabelSpecial returns true if +// a domain name label byte should be prefixed +// with an escaping backslash. +func isDomainNameLabelSpecial(b byte) bool { + switch b { + case '.', ' ', '\'', '@', ';', '(', ')', '"', '\\': + return true + } + return false +} + func nextByte(s string, offset int) (byte, int) { if offset >= len(s) { return 0, 0 @@ -759,8 +761,8 @@ type LOC struct { Altitude uint32 } -// cmToM takes a cm value expressed in RFC1876 SIZE mantissa/exponent -// format and returns a string in m (two decimals for the cm) +// cmToM takes a cm value expressed in RFC 1876 SIZE mantissa/exponent +// format and returns a string in m (two decimals for the cm). func cmToM(m, e uint8) string { if e < 2 { if e == 1 { @@ -1118,6 +1120,7 @@ type URI struct { Target string `dns:"octet"` } +// rr.Target to be parsed as a sequence of character encoded octets according to RFC 3986 func (rr *URI) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target) @@ -1279,6 +1282,7 @@ type CAA struct { Value string `dns:"octet"` } +// rr.Value Is the character-string encoding of the value field as specified in RFC 1035, Section 5.1. func (rr *CAA) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value) } diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index cab46b4..7dd9bbc 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,13 +3,13 @@ package dns import "fmt" // Version is current version of this library. -var Version = V{1, 1, 27} +var Version = v{1, 1, 30} -// V holds the version of this library. -type V struct { +// v holds the version of this library. +type v struct { Major, Minor, Patch int } -func (v V) String() string { +func (v v) String() string { return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) } diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go index a58a8c0..d7ec2d9 100644 --- a/vendor/github.com/miekg/dns/zduplicate.go +++ b/vendor/github.com/miekg/dns/zduplicate.go @@ -104,6 +104,48 @@ func (r1 *CAA) isDuplicate(_r2 RR) bool { return true } +func (r1 *CDNSKEY) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*CDNSKEY) + if !ok { + return false + } + _ = r2 + if r1.Flags != r2.Flags { + return false + } + if r1.Protocol != r2.Protocol { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.PublicKey != r2.PublicKey { + return false + } + return true +} + +func (r1 *CDS) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*CDS) + if !ok { + return false + } + _ = r2 + if r1.KeyTag != r2.KeyTag { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.DigestType != r2.DigestType { + return false + } + if r1.Digest != r2.Digest { + return false + } + return true +} + func (r1 *CERT) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*CERT) if !ok { @@ -172,6 +214,27 @@ func (r1 *DHCID) isDuplicate(_r2 RR) bool { return true } +func (r1 *DLV) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*DLV) + if !ok { + return false + } + _ = r2 + if r1.KeyTag != r2.KeyTag { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.DigestType != r2.DigestType { + return false + } + if r1.Digest != r2.Digest { + return false + } + return true +} + func (r1 *DNAME) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*DNAME) if !ok { @@ -339,6 +402,27 @@ func (r1 *HIP) isDuplicate(_r2 RR) bool { return true } +func (r1 *KEY) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*KEY) + if !ok { + return false + } + _ = r2 + if r1.Flags != r2.Flags { + return false + } + if r1.Protocol != r2.Protocol { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.PublicKey != r2.PublicKey { + return false + } + return true +} + func (r1 *KX) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*KX) if !ok { @@ -849,6 +933,42 @@ func (r1 *RT) isDuplicate(_r2 RR) bool { return true } +func (r1 *SIG) isDuplicate(_r2 RR) bool { + r2, ok := _r2.(*SIG) + if !ok { + return false + } + _ = r2 + if r1.TypeCovered != r2.TypeCovered { + return false + } + if r1.Algorithm != r2.Algorithm { + return false + } + if r1.Labels != r2.Labels { + return false + } + if r1.OrigTtl != r2.OrigTtl { + return false + } + if r1.Expiration != r2.Expiration { + return false + } + if r1.Inception != r2.Inception { + return false + } + if r1.KeyTag != r2.KeyTag { + return false + } + if !isDuplicateName(r1.SignerName, r2.SignerName) { + return false + } + if r1.Signature != r2.Signature { + return false + } + return true +} + func (r1 *SMIMEA) isDuplicate(_r2 RR) bool { r2, ok := _r2.(*SMIMEA) if !ok { diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go index 1cbd6d3..5bb59fa 100644 --- a/vendor/github.com/miekg/dns/ztypes.go +++ b/vendor/github.com/miekg/dns/ztypes.go @@ -685,8 +685,8 @@ func (rr *ANY) copy() RR { } func (rr *APL) copy() RR { Prefixes := make([]APLPrefix, len(rr.Prefixes)) - for i := range rr.Prefixes { - Prefixes[i] = rr.Prefixes[i].copy() + for i, e := range rr.Prefixes { + Prefixes[i] = e.copy() } return &APL{rr.Hdr, Prefixes} } @@ -698,6 +698,12 @@ func (rr *AVC) copy() RR { func (rr *CAA) copy() RR { return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value} } +func (rr *CDNSKEY) copy() RR { + return &CDNSKEY{*rr.DNSKEY.copy().(*DNSKEY)} +} +func (rr *CDS) copy() RR { + return &CDS{*rr.DS.copy().(*DS)} +} func (rr *CERT) copy() RR { return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} } @@ -712,6 +718,9 @@ func (rr *CSYNC) copy() RR { func (rr *DHCID) copy() RR { return &DHCID{rr.Hdr, rr.Digest} } +func (rr *DLV) copy() RR { + return &DLV{*rr.DS.copy().(*DS)} +} func (rr *DNAME) copy() RR { return &DNAME{rr.Hdr, rr.Target} } @@ -744,6 +753,9 @@ func (rr *HIP) copy() RR { copy(RendezvousServers, rr.RendezvousServers) return &HIP{rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers} } +func (rr *KEY) copy() RR { + return &KEY{*rr.DNSKEY.copy().(*DNSKEY)} +} func (rr *KX) copy() RR { return &KX{rr.Hdr, rr.Preference, rr.Exchanger} } @@ -847,6 +859,9 @@ func (rr *RRSIG) copy() RR { func (rr *RT) copy() RR { return &RT{rr.Hdr, rr.Preference, rr.Host} } +func (rr *SIG) copy() RR { + return &SIG{*rr.RRSIG.copy().(*RRSIG)} +} func (rr *SMIMEA) copy() RR { return &SMIMEA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} } diff --git a/vendor/go.uber.org/atomic/.codecov.yml b/vendor/go.uber.org/atomic/.codecov.yml new file mode 100644 index 0000000..6d4d1be --- /dev/null +++ b/vendor/go.uber.org/atomic/.codecov.yml @@ -0,0 +1,15 @@ +coverage: + range: 80..100 + round: down + precision: 2 + + status: + project: # measuring the overall project coverage + default: # context, you can create multiple ones with custom titles + enabled: yes # must be yes|true to enable this status + target: 100 # specify the target coverage for each commit status + # option: "auto" (must increase from parent commit or pull request base) + # option: "X%" a static target percentage to hit + if_not_found: success # if parent is not found report status as success, error, or failure + if_ci_failed: error # if ci fails report status as success, error, or failure + diff --git a/vendor/go.uber.org/atomic/.gitignore b/vendor/go.uber.org/atomic/.gitignore new file mode 100644 index 0000000..c3fa253 --- /dev/null +++ b/vendor/go.uber.org/atomic/.gitignore @@ -0,0 +1,12 @@ +/bin +.DS_Store +/vendor +cover.html +cover.out +lint.log + +# Binaries +*.test + +# Profiling output +*.prof diff --git a/vendor/go.uber.org/atomic/.travis.yml b/vendor/go.uber.org/atomic/.travis.yml new file mode 100644 index 0000000..4e73268 --- /dev/null +++ b/vendor/go.uber.org/atomic/.travis.yml @@ -0,0 +1,27 @@ +sudo: false +language: go +go_import_path: go.uber.org/atomic + +env: + global: + - GO111MODULE=on + +matrix: + include: + - go: 1.12.x + - go: 1.13.x + env: LINT=1 + +cache: + directories: + - vendor + +before_install: + - go version + +script: + - test -z "$LINT" || make lint + - make cover + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/go.uber.org/atomic/CHANGELOG.md b/vendor/go.uber.org/atomic/CHANGELOG.md new file mode 100644 index 0000000..aef8b6e --- /dev/null +++ b/vendor/go.uber.org/atomic/CHANGELOG.md @@ -0,0 +1,64 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.6.0] - 2020-02-24 +### Changed +- Drop library dependency on `golang.org/x/{lint, tools}`. + +## [1.5.1] - 2019-11-19 +- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together + causing `CAS` to fail even though the old value matches. + +## [1.5.0] - 2019-10-29 +### Changed +- With Go modules, only the `go.uber.org/atomic` import path is supported now. + If you need to use the old import path, please add a `replace` directive to + your `go.mod`. + +## [1.4.0] - 2019-05-01 +### Added + - Add `atomic.Error` type for atomic operations on `error` values. + +## [1.3.2] - 2018-05-02 +### Added +- Add `atomic.Duration` type for atomic operations on `time.Duration` values. + +## [1.3.1] - 2017-11-14 +### Fixed +- Revert optimization for `atomic.String.Store("")` which caused data races. + +## [1.3.0] - 2017-11-13 +### Added +- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools. + +### Changed +- Optimize `atomic.String.Store("")` by avoiding an allocation. + +## [1.2.0] - 2017-04-12 +### Added +- Shadow `atomic.Value` from `sync/atomic`. + +## [1.1.0] - 2017-03-10 +### Added +- Add atomic `Float64` type. + +### Changed +- Support new `go.uber.org/atomic` import path. + +## [1.0.0] - 2016-07-18 + +- Initial release. + +[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0 +[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1 +[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0 +[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0 +[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2 +[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1 +[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0 diff --git a/vendor/go.uber.org/atomic/LICENSE.txt b/vendor/go.uber.org/atomic/LICENSE.txt new file mode 100644 index 0000000..8765c9f --- /dev/null +++ b/vendor/go.uber.org/atomic/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/go.uber.org/atomic/Makefile b/vendor/go.uber.org/atomic/Makefile new file mode 100644 index 0000000..39af0fb --- /dev/null +++ b/vendor/go.uber.org/atomic/Makefile @@ -0,0 +1,35 @@ +# Directory to place `go install`ed binaries into. +export GOBIN ?= $(shell pwd)/bin + +GOLINT = $(GOBIN)/golint + +GO_FILES ?= *.go + +.PHONY: build +build: + go build ./... + +.PHONY: test +test: + go test -race ./... + +.PHONY: gofmt +gofmt: + $(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX)) + gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true + @[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false) + +$(GOLINT): + go install golang.org/x/lint/golint + +.PHONY: golint +golint: $(GOLINT) + $(GOLINT) ./... + +.PHONY: lint +lint: gofmt golint + +.PHONY: cover +cover: + go test -coverprofile=cover.out -coverpkg ./... -v ./... + go tool cover -html=cover.out -o cover.html diff --git a/vendor/go.uber.org/atomic/README.md b/vendor/go.uber.org/atomic/README.md new file mode 100644 index 0000000..ade0c20 --- /dev/null +++ b/vendor/go.uber.org/atomic/README.md @@ -0,0 +1,63 @@ +# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard] + +Simple wrappers for primitive types to enforce atomic access. + +## Installation + +```shell +$ go get -u go.uber.org/atomic@v1 +``` + +### Legacy Import Path + +As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way +of using this package. If you are using Go modules, this package will fail to +compile with the legacy import path path `github.com/uber-go/atomic`. + +We recommend migrating your code to the new import path but if you're unable +to do so, or if your dependencies are still using the old import path, you +will have to add a `replace` directive to your `go.mod` file downgrading the +legacy import path to an older version. + +``` +replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0 +``` + +You can do so automatically by running the following command. + +```shell +$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0 +``` + +## Usage + +The standard library's `sync/atomic` is powerful, but it's easy to forget which +variables must be accessed atomically. `go.uber.org/atomic` preserves all the +functionality of the standard library, but wraps the primitive types to +provide a safer, more convenient API. + +```go +var atom atomic.Uint32 +atom.Store(42) +atom.Sub(2) +atom.CAS(40, 11) +``` + +See the [documentation][doc] for a complete API specification. + +## Development Status + +Stable. + +--- + +Released under the [MIT License](LICENSE.txt). + +[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg +[doc]: https://godoc.org/go.uber.org/atomic +[ci-img]: https://travis-ci.com/uber-go/atomic.svg?branch=master +[ci]: https://travis-ci.com/uber-go/atomic +[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/uber-go/atomic +[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic +[reportcard]: https://goreportcard.com/report/go.uber.org/atomic diff --git a/vendor/go.uber.org/atomic/atomic.go b/vendor/go.uber.org/atomic/atomic.go new file mode 100644 index 0000000..ad5fa09 --- /dev/null +++ b/vendor/go.uber.org/atomic/atomic.go @@ -0,0 +1,356 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package atomic provides simple wrappers around numerics to enforce atomic +// access. +package atomic + +import ( + "math" + "sync/atomic" + "time" +) + +// Int32 is an atomic wrapper around an int32. +type Int32 struct{ v int32 } + +// NewInt32 creates an Int32. +func NewInt32(i int32) *Int32 { + return &Int32{i} +} + +// Load atomically loads the wrapped value. +func (i *Int32) Load() int32 { + return atomic.LoadInt32(&i.v) +} + +// Add atomically adds to the wrapped int32 and returns the new value. +func (i *Int32) Add(n int32) int32 { + return atomic.AddInt32(&i.v, n) +} + +// Sub atomically subtracts from the wrapped int32 and returns the new value. +func (i *Int32) Sub(n int32) int32 { + return atomic.AddInt32(&i.v, -n) +} + +// Inc atomically increments the wrapped int32 and returns the new value. +func (i *Int32) Inc() int32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Int32) Dec() int32 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int32) CAS(old, new int32) bool { + return atomic.CompareAndSwapInt32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int32) Store(n int32) { + atomic.StoreInt32(&i.v, n) +} + +// Swap atomically swaps the wrapped int32 and returns the old value. +func (i *Int32) Swap(n int32) int32 { + return atomic.SwapInt32(&i.v, n) +} + +// Int64 is an atomic wrapper around an int64. +type Int64 struct{ v int64 } + +// NewInt64 creates an Int64. +func NewInt64(i int64) *Int64 { + return &Int64{i} +} + +// Load atomically loads the wrapped value. +func (i *Int64) Load() int64 { + return atomic.LoadInt64(&i.v) +} + +// Add atomically adds to the wrapped int64 and returns the new value. +func (i *Int64) Add(n int64) int64 { + return atomic.AddInt64(&i.v, n) +} + +// Sub atomically subtracts from the wrapped int64 and returns the new value. +func (i *Int64) Sub(n int64) int64 { + return atomic.AddInt64(&i.v, -n) +} + +// Inc atomically increments the wrapped int64 and returns the new value. +func (i *Int64) Inc() int64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int64 and returns the new value. +func (i *Int64) Dec() int64 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int64) CAS(old, new int64) bool { + return atomic.CompareAndSwapInt64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int64) Store(n int64) { + atomic.StoreInt64(&i.v, n) +} + +// Swap atomically swaps the wrapped int64 and returns the old value. +func (i *Int64) Swap(n int64) int64 { + return atomic.SwapInt64(&i.v, n) +} + +// Uint32 is an atomic wrapper around an uint32. +type Uint32 struct{ v uint32 } + +// NewUint32 creates a Uint32. +func NewUint32(i uint32) *Uint32 { + return &Uint32{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint32) Load() uint32 { + return atomic.LoadUint32(&i.v) +} + +// Add atomically adds to the wrapped uint32 and returns the new value. +func (i *Uint32) Add(n uint32) uint32 { + return atomic.AddUint32(&i.v, n) +} + +// Sub atomically subtracts from the wrapped uint32 and returns the new value. +func (i *Uint32) Sub(n uint32) uint32 { + return atomic.AddUint32(&i.v, ^(n - 1)) +} + +// Inc atomically increments the wrapped uint32 and returns the new value. +func (i *Uint32) Inc() uint32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Uint32) Dec() uint32 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint32) CAS(old, new uint32) bool { + return atomic.CompareAndSwapUint32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint32) Store(n uint32) { + atomic.StoreUint32(&i.v, n) +} + +// Swap atomically swaps the wrapped uint32 and returns the old value. +func (i *Uint32) Swap(n uint32) uint32 { + return atomic.SwapUint32(&i.v, n) +} + +// Uint64 is an atomic wrapper around a uint64. +type Uint64 struct{ v uint64 } + +// NewUint64 creates a Uint64. +func NewUint64(i uint64) *Uint64 { + return &Uint64{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint64) Load() uint64 { + return atomic.LoadUint64(&i.v) +} + +// Add atomically adds to the wrapped uint64 and returns the new value. +func (i *Uint64) Add(n uint64) uint64 { + return atomic.AddUint64(&i.v, n) +} + +// Sub atomically subtracts from the wrapped uint64 and returns the new value. +func (i *Uint64) Sub(n uint64) uint64 { + return atomic.AddUint64(&i.v, ^(n - 1)) +} + +// Inc atomically increments the wrapped uint64 and returns the new value. +func (i *Uint64) Inc() uint64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped uint64 and returns the new value. +func (i *Uint64) Dec() uint64 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint64) CAS(old, new uint64) bool { + return atomic.CompareAndSwapUint64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint64) Store(n uint64) { + atomic.StoreUint64(&i.v, n) +} + +// Swap atomically swaps the wrapped uint64 and returns the old value. +func (i *Uint64) Swap(n uint64) uint64 { + return atomic.SwapUint64(&i.v, n) +} + +// Bool is an atomic Boolean. +type Bool struct{ v uint32 } + +// NewBool creates a Bool. +func NewBool(initial bool) *Bool { + return &Bool{boolToInt(initial)} +} + +// Load atomically loads the Boolean. +func (b *Bool) Load() bool { + return truthy(atomic.LoadUint32(&b.v)) +} + +// CAS is an atomic compare-and-swap. +func (b *Bool) CAS(old, new bool) bool { + return atomic.CompareAndSwapUint32(&b.v, boolToInt(old), boolToInt(new)) +} + +// Store atomically stores the passed value. +func (b *Bool) Store(new bool) { + atomic.StoreUint32(&b.v, boolToInt(new)) +} + +// Swap sets the given value and returns the previous value. +func (b *Bool) Swap(new bool) bool { + return truthy(atomic.SwapUint32(&b.v, boolToInt(new))) +} + +// Toggle atomically negates the Boolean and returns the previous value. +func (b *Bool) Toggle() bool { + for { + old := b.Load() + if b.CAS(old, !old) { + return old + } + } +} + +func truthy(n uint32) bool { + return n == 1 +} + +func boolToInt(b bool) uint32 { + if b { + return 1 + } + return 0 +} + +// Float64 is an atomic wrapper around float64. +type Float64 struct { + v uint64 +} + +// NewFloat64 creates a Float64. +func NewFloat64(f float64) *Float64 { + return &Float64{math.Float64bits(f)} +} + +// Load atomically loads the wrapped value. +func (f *Float64) Load() float64 { + return math.Float64frombits(atomic.LoadUint64(&f.v)) +} + +// Store atomically stores the passed value. +func (f *Float64) Store(s float64) { + atomic.StoreUint64(&f.v, math.Float64bits(s)) +} + +// Add atomically adds to the wrapped float64 and returns the new value. +func (f *Float64) Add(s float64) float64 { + for { + old := f.Load() + new := old + s + if f.CAS(old, new) { + return new + } + } +} + +// Sub atomically subtracts from the wrapped float64 and returns the new value. +func (f *Float64) Sub(s float64) float64 { + return f.Add(-s) +} + +// CAS is an atomic compare-and-swap. +func (f *Float64) CAS(old, new float64) bool { + return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new)) +} + +// Duration is an atomic wrapper around time.Duration +// https://godoc.org/time#Duration +type Duration struct { + v Int64 +} + +// NewDuration creates a Duration. +func NewDuration(d time.Duration) *Duration { + return &Duration{v: *NewInt64(int64(d))} +} + +// Load atomically loads the wrapped value. +func (d *Duration) Load() time.Duration { + return time.Duration(d.v.Load()) +} + +// Store atomically stores the passed value. +func (d *Duration) Store(n time.Duration) { + d.v.Store(int64(n)) +} + +// Add atomically adds to the wrapped time.Duration and returns the new value. +func (d *Duration) Add(n time.Duration) time.Duration { + return time.Duration(d.v.Add(int64(n))) +} + +// Sub atomically subtracts from the wrapped time.Duration and returns the new value. +func (d *Duration) Sub(n time.Duration) time.Duration { + return time.Duration(d.v.Sub(int64(n))) +} + +// Swap atomically swaps the wrapped time.Duration and returns the old value. +func (d *Duration) Swap(n time.Duration) time.Duration { + return time.Duration(d.v.Swap(int64(n))) +} + +// CAS is an atomic compare-and-swap. +func (d *Duration) CAS(old, new time.Duration) bool { + return d.v.CAS(int64(old), int64(new)) +} + +// Value shadows the type of the same name from sync/atomic +// https://godoc.org/sync/atomic#Value +type Value struct{ atomic.Value } diff --git a/vendor/go.uber.org/atomic/error.go b/vendor/go.uber.org/atomic/error.go new file mode 100644 index 0000000..0489d19 --- /dev/null +++ b/vendor/go.uber.org/atomic/error.go @@ -0,0 +1,55 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +// Error is an atomic type-safe wrapper around Value for errors +type Error struct{ v Value } + +// errorHolder is non-nil holder for error object. +// atomic.Value panics on saving nil object, so err object needs to be +// wrapped with valid object first. +type errorHolder struct{ err error } + +// NewError creates new atomic error object +func NewError(err error) *Error { + e := &Error{} + if err != nil { + e.Store(err) + } + return e +} + +// Load atomically loads the wrapped error +func (e *Error) Load() error { + v := e.v.Load() + if v == nil { + return nil + } + + eh := v.(errorHolder) + return eh.err +} + +// Store atomically stores error. +// NOTE: a holder object is allocated on each Store call. +func (e *Error) Store(err error) { + e.v.Store(errorHolder{err: err}) +} diff --git a/vendor/go.uber.org/atomic/go.mod b/vendor/go.uber.org/atomic/go.mod new file mode 100644 index 0000000..a935dae --- /dev/null +++ b/vendor/go.uber.org/atomic/go.mod @@ -0,0 +1,10 @@ +module go.uber.org/atomic + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/stretchr/testify v1.3.0 + golang.org/x/lint v0.0.0-20190930215403-16217165b5de + golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c // indirect +) + +go 1.13 diff --git a/vendor/go.uber.org/atomic/go.sum b/vendor/go.uber.org/atomic/go.sum new file mode 100644 index 0000000..51b2b62 --- /dev/null +++ b/vendor/go.uber.org/atomic/go.sum @@ -0,0 +1,22 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/go.uber.org/atomic/string.go b/vendor/go.uber.org/atomic/string.go new file mode 100644 index 0000000..ede8136 --- /dev/null +++ b/vendor/go.uber.org/atomic/string.go @@ -0,0 +1,49 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +// String is an atomic type-safe wrapper around Value for strings. +type String struct{ v Value } + +// NewString creates a String. +func NewString(str string) *String { + s := &String{} + if str != "" { + s.Store(str) + } + return s +} + +// Load atomically loads the wrapped string. +func (s *String) Load() string { + v := s.v.Load() + if v == nil { + return "" + } + return v.(string) +} + +// Store atomically stores the passed string. +// Note: Converting the string to an interface{} to store in the Value +// requires an allocation. +func (s *String) Store(str string) { + s.v.Store(str) +} diff --git a/vendor/go.uber.org/multierr/.codecov.yml b/vendor/go.uber.org/multierr/.codecov.yml new file mode 100644 index 0000000..6d4d1be --- /dev/null +++ b/vendor/go.uber.org/multierr/.codecov.yml @@ -0,0 +1,15 @@ +coverage: + range: 80..100 + round: down + precision: 2 + + status: + project: # measuring the overall project coverage + default: # context, you can create multiple ones with custom titles + enabled: yes # must be yes|true to enable this status + target: 100 # specify the target coverage for each commit status + # option: "auto" (must increase from parent commit or pull request base) + # option: "X%" a static target percentage to hit + if_not_found: success # if parent is not found report status as success, error, or failure + if_ci_failed: error # if ci fails report status as success, error, or failure + diff --git a/vendor/go.uber.org/multierr/.gitignore b/vendor/go.uber.org/multierr/.gitignore new file mode 100644 index 0000000..b9a05e3 --- /dev/null +++ b/vendor/go.uber.org/multierr/.gitignore @@ -0,0 +1,4 @@ +/vendor +cover.html +cover.out +/bin diff --git a/vendor/go.uber.org/multierr/.travis.yml b/vendor/go.uber.org/multierr/.travis.yml new file mode 100644 index 0000000..786c917 --- /dev/null +++ b/vendor/go.uber.org/multierr/.travis.yml @@ -0,0 +1,29 @@ +sudo: false +language: go +go_import_path: go.uber.org/multierr + +env: + global: + - GO15VENDOREXPERIMENT=1 + - GO111MODULE=on + +go: + - 1.11.x + - 1.12.x + - 1.13.x + +cache: + directories: + - vendor + +before_install: +- go version + +script: +- | + set -e + make lint + make cover + +after_success: +- bash <(curl -s https://codecov.io/bash) diff --git a/vendor/go.uber.org/multierr/CHANGELOG.md b/vendor/go.uber.org/multierr/CHANGELOG.md new file mode 100644 index 0000000..3110c5a --- /dev/null +++ b/vendor/go.uber.org/multierr/CHANGELOG.md @@ -0,0 +1,54 @@ +Releases +======== + +v1.5.0 (2020-02-24) +=================== + +- Drop library dependency on development-time tooling. + + +v1.4.0 (2019-11-04) +=================== + +- Add `AppendInto` function to more ergonomically build errors inside a + loop. + + +v1.3.0 (2019-10-29) +=================== + +- Switch to Go modules. + + +v1.2.0 (2019-09-26) +=================== + +- Support extracting and matching against wrapped errors with `errors.As` + and `errors.Is`. + + +v1.1.0 (2017-06-30) +=================== + +- Added an `Errors(error) []error` function to extract the underlying list of + errors for a multierr error. + + +v1.0.0 (2017-05-31) +=================== + +No changes since v0.2.0. This release is committing to making no breaking +changes to the current API in the 1.X series. + + +v0.2.0 (2017-04-11) +=================== + +- Repeatedly appending to the same error is now faster due to fewer + allocations. + + +v0.1.0 (2017-31-03) +=================== + +- Initial release diff --git a/vendor/go.uber.org/multierr/LICENSE.txt b/vendor/go.uber.org/multierr/LICENSE.txt new file mode 100644 index 0000000..858e024 --- /dev/null +++ b/vendor/go.uber.org/multierr/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/go.uber.org/multierr/Makefile b/vendor/go.uber.org/multierr/Makefile new file mode 100644 index 0000000..4160182 --- /dev/null +++ b/vendor/go.uber.org/multierr/Makefile @@ -0,0 +1,42 @@ +# Directory to put `go install`ed binaries in. +export GOBIN ?= $(shell pwd)/bin + +GO_FILES := $(shell \ + find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ + -o -name '*.go' -print | cut -b3-) + +.PHONY: build +build: + go build ./... + +.PHONY: test +test: + go test -race ./... + +.PHONY: gofmt +gofmt: + $(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX)) + @gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true + @[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false) + +.PHONY: golint +golint: + @go install golang.org/x/lint/golint + @$(GOBIN)/golint ./... + +.PHONY: staticcheck +staticcheck: + @go install honnef.co/go/tools/cmd/staticcheck + @$(GOBIN)/staticcheck ./... + +.PHONY: lint +lint: gofmt golint staticcheck + +.PHONY: cover +cover: + go test -coverprofile=cover.out -coverpkg=./... -v ./... + go tool cover -html=cover.out -o cover.html + +update-license: + @go install go.uber.org/tools/update-license + @$(GOBIN)/update-license $(GO_FILES) diff --git a/vendor/go.uber.org/multierr/README.md b/vendor/go.uber.org/multierr/README.md new file mode 100644 index 0000000..751bd65 --- /dev/null +++ b/vendor/go.uber.org/multierr/README.md @@ -0,0 +1,23 @@ +# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +`multierr` allows combining one or more Go `error`s together. + +## Installation + + go get -u go.uber.org/multierr + +## Status + +Stable: No breaking changes will be made before 2.0. + +------------------------------------------------------------------------------- + +Released under the [MIT License]. + +[MIT License]: LICENSE.txt +[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg +[doc]: https://godoc.org/go.uber.org/multierr +[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master +[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg +[ci]: https://travis-ci.com/uber-go/multierr +[cov]: https://codecov.io/gh/uber-go/multierr diff --git a/vendor/go.uber.org/multierr/error.go b/vendor/go.uber.org/multierr/error.go new file mode 100644 index 0000000..04eb961 --- /dev/null +++ b/vendor/go.uber.org/multierr/error.go @@ -0,0 +1,449 @@ +// Copyright (c) 2019 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package multierr allows combining one or more errors together. +// +// Overview +// +// Errors can be combined with the use of the Combine function. +// +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// conn.Close(), +// ) +// +// If only two errors are being combined, the Append function may be used +// instead. +// +// err = multierr.Append(reader.Close(), writer.Close()) +// +// This makes it possible to record resource cleanup failures from deferred +// blocks with the help of named return values. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer func() { +// err = multierr.Append(err, conn.Close()) +// }() +// // ... +// } +// +// The underlying list of errors for a returned error object may be retrieved +// with the Errors function. +// +// errors := multierr.Errors(err) +// if len(errors) > 0 { +// fmt.Println("The following errors occurred:") +// } +// +// Advanced Usage +// +// Errors returned by Combine and Append MAY implement the following +// interface. +// +// type errorGroup interface { +// // Returns a slice containing the underlying list of errors. +// // +// // This slice MUST NOT be modified by the caller. +// Errors() []error +// } +// +// Note that if you need access to list of errors behind a multierr error, you +// should prefer using the Errors function. That said, if you need cheap +// read-only access to the underlying errors slice, you can attempt to cast +// the error to this interface. You MUST handle the failure case gracefully +// because errors returned by Combine and Append are not guaranteed to +// implement this interface. +// +// var errors []error +// group, ok := err.(errorGroup) +// if ok { +// errors = group.Errors() +// } else { +// errors = []error{err} +// } +package multierr // import "go.uber.org/multierr" + +import ( + "bytes" + "fmt" + "io" + "strings" + "sync" + + "go.uber.org/atomic" +) + +var ( + // Separator for single-line error messages. + _singlelineSeparator = []byte("; ") + + // Prefix for multi-line messages + _multilinePrefix = []byte("the following errors occurred:") + + // Prefix for the first and following lines of an item in a list of + // multi-line error messages. + // + // For example, if a single item is: + // + // foo + // bar + // + // It will become, + // + // - foo + // bar + _multilineSeparator = []byte("\n - ") + _multilineIndent = []byte(" ") +) + +// _bufferPool is a pool of bytes.Buffers. +var _bufferPool = sync.Pool{ + New: func() interface{} { + return &bytes.Buffer{} + }, +} + +type errorGroup interface { + Errors() []error +} + +// Errors returns a slice containing zero or more errors that the supplied +// error is composed of. If the error is nil, a nil slice is returned. +// +// err := multierr.Append(r.Close(), w.Close()) +// errors := multierr.Errors(err) +// +// If the error is not composed of other errors, the returned slice contains +// just the error that was passed in. +// +// Callers of this function are free to modify the returned slice. +func Errors(err error) []error { + if err == nil { + return nil + } + + // Note that we're casting to multiError, not errorGroup. Our contract is + // that returned errors MAY implement errorGroup. Errors, however, only + // has special behavior for multierr-specific error objects. + // + // This behavior can be expanded in the future but I think it's prudent to + // start with as little as possible in terms of contract and possibility + // of misuse. + eg, ok := err.(*multiError) + if !ok { + return []error{err} + } + + errors := eg.Errors() + result := make([]error, len(errors)) + copy(result, errors) + return result +} + +// multiError is an error that holds one or more errors. +// +// An instance of this is guaranteed to be non-empty and flattened. That is, +// none of the errors inside multiError are other multiErrors. +// +// multiError formats to a semi-colon delimited list of error messages with +// %v and with a more readable multi-line format with %+v. +type multiError struct { + copyNeeded atomic.Bool + errors []error +} + +var _ errorGroup = (*multiError)(nil) + +// Errors returns the list of underlying errors. +// +// This slice MUST NOT be modified. +func (merr *multiError) Errors() []error { + if merr == nil { + return nil + } + return merr.errors +} + +func (merr *multiError) Error() string { + if merr == nil { + return "" + } + + buff := _bufferPool.Get().(*bytes.Buffer) + buff.Reset() + + merr.writeSingleline(buff) + + result := buff.String() + _bufferPool.Put(buff) + return result +} + +func (merr *multiError) Format(f fmt.State, c rune) { + if c == 'v' && f.Flag('+') { + merr.writeMultiline(f) + } else { + merr.writeSingleline(f) + } +} + +func (merr *multiError) writeSingleline(w io.Writer) { + first := true + for _, item := range merr.errors { + if first { + first = false + } else { + w.Write(_singlelineSeparator) + } + io.WriteString(w, item.Error()) + } +} + +func (merr *multiError) writeMultiline(w io.Writer) { + w.Write(_multilinePrefix) + for _, item := range merr.errors { + w.Write(_multilineSeparator) + writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item)) + } +} + +// Writes s to the writer with the given prefix added before each line after +// the first. +func writePrefixLine(w io.Writer, prefix []byte, s string) { + first := true + for len(s) > 0 { + if first { + first = false + } else { + w.Write(prefix) + } + + idx := strings.IndexByte(s, '\n') + if idx < 0 { + idx = len(s) - 1 + } + + io.WriteString(w, s[:idx+1]) + s = s[idx+1:] + } +} + +type inspectResult struct { + // Number of top-level non-nil errors + Count int + + // Total number of errors including multiErrors + Capacity int + + // Index of the first non-nil error in the list. Value is meaningless if + // Count is zero. + FirstErrorIdx int + + // Whether the list contains at least one multiError + ContainsMultiError bool +} + +// Inspects the given slice of errors so that we can efficiently allocate +// space for it. +func inspect(errors []error) (res inspectResult) { + first := true + for i, err := range errors { + if err == nil { + continue + } + + res.Count++ + if first { + first = false + res.FirstErrorIdx = i + } + + if merr, ok := err.(*multiError); ok { + res.Capacity += len(merr.errors) + res.ContainsMultiError = true + } else { + res.Capacity++ + } + } + return +} + +// fromSlice converts the given list of errors into a single error. +func fromSlice(errors []error) error { + res := inspect(errors) + switch res.Count { + case 0: + return nil + case 1: + // only one non-nil entry + return errors[res.FirstErrorIdx] + case len(errors): + if !res.ContainsMultiError { + // already flat + return &multiError{errors: errors} + } + } + + nonNilErrs := make([]error, 0, res.Capacity) + for _, err := range errors[res.FirstErrorIdx:] { + if err == nil { + continue + } + + if nested, ok := err.(*multiError); ok { + nonNilErrs = append(nonNilErrs, nested.errors...) + } else { + nonNilErrs = append(nonNilErrs, err) + } + } + + return &multiError{errors: nonNilErrs} +} + +// Combine combines the passed errors into a single error. +// +// If zero arguments were passed or if all items are nil, a nil error is +// returned. +// +// Combine(nil, nil) // == nil +// +// If only a single error was passed, it is returned as-is. +// +// Combine(err) // == err +// +// Combine skips over nil arguments so this function may be used to combine +// together errors from operations that fail independently of each other. +// +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// pipe.Close(), +// ) +// +// If any of the passed errors is a multierr error, it will be flattened along +// with the other errors. +// +// multierr.Combine(multierr.Combine(err1, err2), err3) +// // is the same as +// multierr.Combine(err1, err2, err3) +// +// The returned error formats into a readable multi-line error message if +// formatted with %+v. +// +// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) +func Combine(errors ...error) error { + return fromSlice(errors) +} + +// Append appends the given errors together. Either value may be nil. +// +// This function is a specialization of Combine for the common case where +// there are only two errors. +// +// err = multierr.Append(reader.Close(), writer.Close()) +// +// The following pattern may also be used to record failure of deferred +// operations without losing information about the original error. +// +// func doSomething(..) (err error) { +// f := acquireResource() +// defer func() { +// err = multierr.Append(err, f.Close()) +// }() +func Append(left error, right error) error { + switch { + case left == nil: + return right + case right == nil: + return left + } + + if _, ok := right.(*multiError); !ok { + if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) { + // Common case where the error on the left is constantly being + // appended to. + errs := append(l.errors, right) + return &multiError{errors: errs} + } else if !ok { + // Both errors are single errors. + return &multiError{errors: []error{left, right}} + } + } + + // Either right or both, left and right, are multiErrors. Rely on usual + // expensive logic. + errors := [2]error{left, right} + return fromSlice(errors[0:]) +} + +// AppendInto appends an error into the destination of an error pointer and +// returns whether the error being appended was non-nil. +// +// var err error +// multierr.AppendInto(&err, r.Close()) +// multierr.AppendInto(&err, w.Close()) +// +// The above is equivalent to, +// +// err := multierr.Append(r.Close(), w.Close()) +// +// As AppendInto reports whether the provided error was non-nil, it may be +// used to build a multierr error in a loop more ergonomically. For example: +// +// var err error +// for line := range lines { +// var item Item +// if multierr.AppendInto(&err, parse(line, &item)) { +// continue +// } +// items = append(items, item) +// } +// +// Compare this with a verison that relies solely on Append: +// +// var err error +// for line := range lines { +// var item Item +// if parseErr := parse(line, &item); parseErr != nil { +// err = multierr.Append(err, parseErr) +// continue +// } +// items = append(items, item) +// } +func AppendInto(into *error, err error) (errored bool) { + if into == nil { + // We panic if 'into' is nil. This is not documented above + // because suggesting that the pointer must be non-nil may + // confuse users into thinking that the error that it points + // to must be non-nil. + panic("misuse of multierr.AppendInto: into pointer must not be nil") + } + + if err == nil { + return false + } + *into = Append(*into, err) + return true +} diff --git a/vendor/go.uber.org/multierr/glide.yaml b/vendor/go.uber.org/multierr/glide.yaml new file mode 100644 index 0000000..6ef084e --- /dev/null +++ b/vendor/go.uber.org/multierr/glide.yaml @@ -0,0 +1,8 @@ +package: go.uber.org/multierr +import: +- package: go.uber.org/atomic + version: ^1 +testImport: +- package: github.com/stretchr/testify + subpackages: + - assert diff --git a/vendor/go.uber.org/multierr/go.mod b/vendor/go.uber.org/multierr/go.mod new file mode 100644 index 0000000..58d5f90 --- /dev/null +++ b/vendor/go.uber.org/multierr/go.mod @@ -0,0 +1,12 @@ +module go.uber.org/multierr + +go 1.12 + +require ( + github.com/stretchr/testify v1.3.0 + go.uber.org/atomic v1.6.0 + go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee + golang.org/x/lint v0.0.0-20190930215403-16217165b5de + golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 // indirect + honnef.co/go/tools v0.0.1-2019.2.3 +) diff --git a/vendor/go.uber.org/multierr/go.sum b/vendor/go.uber.org/multierr/go.sum new file mode 100644 index 0000000..557fbba --- /dev/null +++ b/vendor/go.uber.org/multierr/go.sum @@ -0,0 +1,45 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/vendor/go.uber.org/multierr/go113.go b/vendor/go.uber.org/multierr/go113.go new file mode 100644 index 0000000..264b0ea --- /dev/null +++ b/vendor/go.uber.org/multierr/go113.go @@ -0,0 +1,52 @@ +// Copyright (c) 2019 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// +build go1.13 + +package multierr + +import "errors" + +// As attempts to find the first error in the error list that matches the type +// of the value that target points to. +// +// This function allows errors.As to traverse the values stored on the +// multierr error. +func (merr *multiError) As(target interface{}) bool { + for _, err := range merr.Errors() { + if errors.As(err, target) { + return true + } + } + return false +} + +// Is attempts to match the provided error against errors in the error list. +// +// This function allows errors.Is to traverse the values stored on the +// multierr error. +func (merr *multiError) Is(target error) bool { + for _, err := range merr.Errors() { + if errors.Is(err, target) { + return true + } + } + return false +} diff --git a/vendor/go.uber.org/zap/.codecov.yml b/vendor/go.uber.org/zap/.codecov.yml new file mode 100644 index 0000000..8e5ca7d --- /dev/null +++ b/vendor/go.uber.org/zap/.codecov.yml @@ -0,0 +1,17 @@ +coverage: + range: 80..100 + round: down + precision: 2 + + status: + project: # measuring the overall project coverage + default: # context, you can create multiple ones with custom titles + enabled: yes # must be yes|true to enable this status + target: 95% # specify the target coverage for each commit status + # option: "auto" (must increase from parent commit or pull request base) + # option: "X%" a static target percentage to hit + if_not_found: success # if parent is not found report status as success, error, or failure + if_ci_failed: error # if ci fails report status as success, error, or failure +ignore: + - internal/readme/readme.go + diff --git a/vendor/go.uber.org/zap/.gitignore b/vendor/go.uber.org/zap/.gitignore new file mode 100644 index 0000000..da9d9d0 --- /dev/null +++ b/vendor/go.uber.org/zap/.gitignore @@ -0,0 +1,32 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +vendor + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.pprof +*.out +*.log + +/bin +cover.out +cover.html diff --git a/vendor/go.uber.org/zap/.readme.tmpl b/vendor/go.uber.org/zap/.readme.tmpl new file mode 100644 index 0000000..3154a1e --- /dev/null +++ b/vendor/go.uber.org/zap/.readme.tmpl @@ -0,0 +1,109 @@ +# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +Blazing fast, structured, leveled logging in Go. + +## Installation + +`go get -u go.uber.org/zap` + +Note that zap only supports the two most recent minor versions of Go. + +## Quick Start + +In contexts where performance is nice, but not critical, use the +`SugaredLogger`. It's 4-10x faster than other structured logging +packages and includes both structured and `printf`-style APIs. + +```go +logger, _ := zap.NewProduction() +defer logger.Sync() // flushes buffer, if any +sugar := logger.Sugar() +sugar.Infow("failed to fetch URL", + // Structured context as loosely typed key-value pairs. + "url", url, + "attempt", 3, + "backoff", time.Second, +) +sugar.Infof("Failed to fetch URL: %s", url) +``` + +When performance and type safety are critical, use the `Logger`. It's even +faster than the `SugaredLogger` and allocates far less, but it only supports +structured logging. + +```go +logger, _ := zap.NewProduction() +defer logger.Sync() +logger.Info("failed to fetch URL", + // Structured context as strongly typed Field values. + zap.String("url", url), + zap.Int("attempt", 3), + zap.Duration("backoff", time.Second), +) +``` + +See the [documentation][doc] and [FAQ](FAQ.md) for more details. + +## Performance + +For applications that log in the hot path, reflection-based serialization and +string formatting are prohibitively expensive — they're CPU-intensive +and make many small allocations. Put differently, using `encoding/json` and +`fmt.Fprintf` to log tons of `interface{}`s makes your application slow. + +Zap takes a different approach. It includes a reflection-free, zero-allocation +JSON encoder, and the base `Logger` strives to avoid serialization overhead +and allocations wherever possible. By building the high-level `SugaredLogger` +on that foundation, zap lets users *choose* when they need to count every +allocation and when they'd prefer a more familiar, loosely typed API. + +As measured by its own [benchmarking suite][], not only is zap more performant +than comparable structured logging packages — it's also faster than the +standard library. Like all benchmarks, take these with a grain of salt.[1](#footnote-versions) + +Log a message and 10 fields: + +{{.BenchmarkAddingFields}} + +Log a message with a logger that already has 10 fields of context: + +{{.BenchmarkAccumulatedContext}} + +Log a static string, without any context or `printf`-style templating: + +{{.BenchmarkWithoutFields}} + +## Development Status: Stable + +All APIs are finalized, and no breaking changes will be made in the 1.x series +of releases. Users of semver-aware dependency management systems should pin +zap to `^1`. + +## Contributing + +We encourage and support an active, healthy community of contributors — +including you! Details are in the [contribution guide](CONTRIBUTING.md) and +the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on +issues and pull requests, but you can also report any negative conduct to +oss-conduct@uber.com. That email list is a private, safe space; even the zap +maintainers don't have access, so don't hesitate to hold us to a high +standard. + +
+ +Released under the [MIT License](LICENSE.txt). + +1 In particular, keep in mind that we may be +benchmarking against slightly older versions of other packages. Versions are +pinned in zap's [glide.lock][] file. [↩](#anchor-versions) + +[doc-img]: https://godoc.org/go.uber.org/zap?status.svg +[doc]: https://godoc.org/go.uber.org/zap +[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master +[ci]: https://travis-ci.com/uber-go/zap +[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/uber-go/zap +[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks +[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock + diff --git a/vendor/go.uber.org/zap/.travis.yml b/vendor/go.uber.org/zap/.travis.yml new file mode 100644 index 0000000..cfdc69f --- /dev/null +++ b/vendor/go.uber.org/zap/.travis.yml @@ -0,0 +1,23 @@ +language: go +sudo: false + +go_import_path: go.uber.org/zap +env: + global: + - TEST_TIMEOUT_SCALE=10 + - GO111MODULE=on + +matrix: + include: + - go: 1.13.x + - go: 1.14.x + env: LINT=1 + +script: + - test -z "$LINT" || make lint + - make test + - make bench + +after_success: + - make cover + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md new file mode 100644 index 0000000..aeff90e --- /dev/null +++ b/vendor/go.uber.org/zap/CHANGELOG.md @@ -0,0 +1,401 @@ +# Changelog + +## 1.15.0 (23 Apr 2020) + +Bugfixes: +* [#804][]: Fix handling of `Time` values out of `UnixNano` range. +* [#812][]: Fix `IncreaseLevel` being reset after a call to `With`. + +Enhancements: +* [#806][]: Add `WithCaller` option to supersede the `AddCaller` option. This + allows disabling annotation of log entries with caller information if + previously enabled with `AddCaller`. +* [#813][]: Deprecate `NewSampler` constructor in favor of + `NewSamplerWithOptions` which supports a `SamplerHook` option. This option + adds support for monitoring sampling decisions through a hook. + +Thanks to @danielbprice for their contributions to this release. + +## 1.14.1 (14 Mar 2020) + +Bugfixes: +* [#791][]: Fix panic on attempting to build a logger with an invalid Config. +* [#795][]: Vendoring Zap with `go mod vendor` no longer includes Zap's + development-time dependencies. +* [#799][]: Fix issue introduced in 1.14.0 that caused invalid JSON output to + be generated for arrays of `time.Time` objects when using string-based time + formats. + +Thanks to @YashishDua for their contributions to this release. + +## 1.14.0 (20 Feb 2020) + +Enhancements: +* [#771][]: Optimize calls for disabled log levels. +* [#773][]: Add millisecond duration encoder. +* [#775][]: Add option to increase the level of a logger. +* [#786][]: Optimize time formatters using `Time.AppendFormat` where possible. + +Thanks to @caibirdme for their contributions to this release. + +## 1.13.0 (13 Nov 2019) + +Enhancements: +* [#758][]: Add `Intp`, `Stringp`, and other similar `*p` field constructors + to log pointers to primitives with support for `nil` values. + +Thanks to @jbizzle for their contributions to this release. + +## 1.12.0 (29 Oct 2019) + +Enhancements: +* [#751][]: Migrate to Go modules. + +## 1.11.0 (21 Oct 2019) + +Enhancements: +* [#725][]: Add `zapcore.OmitKey` to omit keys in an `EncoderConfig`. +* [#736][]: Add `RFC3339` and `RFC3339Nano` time encoders. + +Thanks to @juicemia, @uhthomas for their contributions to this release. + +## 1.10.0 (29 Apr 2019) + +Bugfixes: +* [#657][]: Fix `MapObjectEncoder.AppendByteString` not adding value as a + string. +* [#706][]: Fix incorrect call depth to determine caller in Go 1.12. + +Enhancements: +* [#610][]: Add `zaptest.WrapOptions` to wrap `zap.Option` for creating test + loggers. +* [#675][]: Don't panic when encoding a String field. +* [#704][]: Disable HTML escaping for JSON objects encoded using the + reflect-based encoder. + +Thanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions +to this release. + +## v1.9.1 (06 Aug 2018) + +Bugfixes: + +* [#614][]: MapObjectEncoder should not ignore empty slices. + +## v1.9.0 (19 Jul 2018) + +Enhancements: +* [#602][]: Reduce number of allocations when logging with reflection. +* [#572][], [#606][]: Expose a registry for third-party logging sinks. + +Thanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and +@dimroc for their contributions to this release. + +## v1.8.0 (13 Apr 2018) + +Enhancements: +* [#508][]: Make log level configurable when redirecting the standard + library's logger. +* [#518][]: Add a logger that writes to a `*testing.TB`. +* [#577][]: Add a top-level alias for `zapcore.Field` to clean up GoDoc. + +Bugfixes: +* [#574][]: Add a missing import comment to `go.uber.org/zap/buffer`. + +Thanks to @DiSiqueira and @djui for their contributions to this release. + +## v1.7.1 (25 Sep 2017) + +Bugfixes: +* [#504][]: Store strings when using AddByteString with the map encoder. + +## v1.7.0 (21 Sep 2017) + +Enhancements: + +* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user + to specify the level of the logged messages. + +## v1.6.0 (30 Aug 2017) + +Enhancements: + +* [#491][]: Omit zap stack frames from stacktraces. +* [#490][]: Add a `ContextMap` method to observer logs for simpler + field validation in tests. + +## v1.5.0 (22 Jul 2017) + +Enhancements: + +* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`. +* [#465][]: Support user-supplied encoders for logger names. + +Bugfixes: + +* [#477][]: Fix a bug that incorrectly truncated deep stacktraces. + +Thanks to @richard-tunein and @pavius for their contributions to this release. + +## v1.4.1 (08 Jun 2017) + +This release fixes two bugs. + +Bugfixes: + +* [#435][]: Support a variety of case conventions when unmarshaling levels. +* [#444][]: Fix a panic in the observer. + +## v1.4.0 (12 May 2017) + +This release adds a few small features and is fully backward-compatible. + +Enhancements: + +* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to + override the Unix-style default. +* [#425][]: Preserve time zones when logging times. +* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a + variety of operations a bit simpler. + +## v1.3.0 (25 Apr 2017) + +This release adds an enhancement to zap's testing helpers as well as the +ability to marshal an AtomicLevel. It is fully backward-compatible. + +Enhancements: + +* [#415][]: Add a substring-filtering helper to zap's observer. This is + particularly useful when testing the `SugaredLogger`. +* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`. + +## v1.2.0 (13 Apr 2017) + +This release adds a gRPC compatibility wrapper. It is fully backward-compatible. + +Enhancements: + +* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements + `grpclog.Logger`. + +## v1.1.0 (31 Mar 2017) + +This release fixes two bugs and adds some enhancements to zap's testing helpers. +It is fully backward-compatible. + +Bugfixes: + +* [#385][]: Fix caller path trimming on Windows. +* [#396][]: Fix a panic when attempting to use non-existent directories with + zap's configuration struct. + +Enhancements: + +* [#386][]: Add filtering helpers to zaptest's observing logger. + +Thanks to @moitias for contributing to this release. + +## v1.0.0 (14 Mar 2017) + +This is zap's first stable release. All exported APIs are now final, and no +further breaking changes will be made in the 1.x release series. Anyone using a +semver-aware dependency manager should now pin to `^1`. + +Breaking changes: + +* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without + casting from `[]byte` to `string`. +* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`, + `zap.Logger`, and `zap.SugaredLogger`. +* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to + clash with other testing helpers. + +Bugfixes: + +* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier + for tab-separated console output. +* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to + work with concurrency-safe `WriteSyncer` implementations. +* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux + systems. +* [#373][]: Report the correct caller from zap's standard library + interoperability wrappers. + +Enhancements: + +* [#348][]: Add a registry allowing third-party encodings to work with zap's + built-in `Config`. +* [#327][]: Make the representation of logger callers configurable (like times, + levels, and durations). +* [#376][]: Allow third-party encoders to use their own buffer pools, which + removes the last performance advantage that zap's encoders have over plugins. +* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple + `WriteSyncer`s and lock the result. +* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in + Go 1.9). +* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it + easier for particularly punctilious users to unit test their application's + logging. + +Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their +contributions to this release. + +## v1.0.0-rc.3 (7 Mar 2017) + +This is the third release candidate for zap's stable release. There are no +breaking changes. + +Bugfixes: + +* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs + rather than `[]uint8`. + +Enhancements: + +* [#307][]: Users can opt into colored output for log levels. +* [#353][]: In addition to hijacking the output of the standard library's + package-global logging functions, users can now construct a zap-backed + `log.Logger` instance. +* [#311][]: Frames from common runtime functions and some of zap's internal + machinery are now omitted from stacktraces. + +Thanks to @ansel1 and @suyash for their contributions to this release. + +## v1.0.0-rc.2 (21 Feb 2017) + +This is the second release candidate for zap's stable release. It includes two +breaking changes. + +Breaking changes: + +* [#316][]: Zap's global loggers are now fully concurrency-safe + (previously, users had to ensure that `ReplaceGlobals` was called before the + loggers were in use). However, they must now be accessed via the `L()` and + `S()` functions. Users can update their projects with + + ``` + gofmt -r "zap.L -> zap.L()" -w . + gofmt -r "zap.S -> zap.S()" -w . + ``` +* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid + JSON and YAML struct tags on all config structs. This release fixes the tags + and adds static analysis to prevent similar bugs in the future. + +Bugfixes: + +* [#321][]: Redirecting the standard library's `log` output now + correctly reports the logger's caller. + +Enhancements: + +* [#325][] and [#333][]: Zap now transparently supports non-standard, rich + errors like those produced by `github.com/pkg/errors`. +* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is + now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) -> + zap.NewNop()' -w .`. +* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a + more informative error. + +Thanks to @skipor and @chapsuk for their contributions to this release. + +## v1.0.0-rc.1 (14 Feb 2017) + +This is the first release candidate for zap's stable release. There are multiple +breaking changes and improvements from the pre-release version. Most notably: + +* **Zap's import path is now "go.uber.org/zap"** — all users will + need to update their code. +* User-facing types and functions remain in the `zap` package. Code relevant + largely to extension authors is now in the `zapcore` package. +* The `zapcore.Core` type makes it easy for third-party packages to use zap's + internals but provide a different user-facing API. +* `Logger` is now a concrete type instead of an interface. +* A less verbose (though slower) logging API is included by default. +* Package-global loggers `L` and `S` are included. +* A human-friendly console encoder is included. +* A declarative config struct allows common logger configurations to be managed + as configuration instead of code. +* Sampling is more accurate, and doesn't depend on the standard library's shared + timer heap. + +## v0.1.0-beta.1 (6 Feb 2017) + +This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and +upgrade at their leisure. Since this is the first tagged release, there are no +backward compatibility concerns and all functionality is new. + +Early zap adopters should pin to the 0.1.x minor version until they're ready to +upgrade to the upcoming stable release. + +[#316]: https://github.com/uber-go/zap/pull/316 +[#309]: https://github.com/uber-go/zap/pull/309 +[#317]: https://github.com/uber-go/zap/pull/317 +[#321]: https://github.com/uber-go/zap/pull/321 +[#325]: https://github.com/uber-go/zap/pull/325 +[#333]: https://github.com/uber-go/zap/pull/333 +[#326]: https://github.com/uber-go/zap/pull/326 +[#300]: https://github.com/uber-go/zap/pull/300 +[#339]: https://github.com/uber-go/zap/pull/339 +[#307]: https://github.com/uber-go/zap/pull/307 +[#353]: https://github.com/uber-go/zap/pull/353 +[#311]: https://github.com/uber-go/zap/pull/311 +[#366]: https://github.com/uber-go/zap/pull/366 +[#364]: https://github.com/uber-go/zap/pull/364 +[#371]: https://github.com/uber-go/zap/pull/371 +[#362]: https://github.com/uber-go/zap/pull/362 +[#369]: https://github.com/uber-go/zap/pull/369 +[#347]: https://github.com/uber-go/zap/pull/347 +[#373]: https://github.com/uber-go/zap/pull/373 +[#348]: https://github.com/uber-go/zap/pull/348 +[#327]: https://github.com/uber-go/zap/pull/327 +[#376]: https://github.com/uber-go/zap/pull/376 +[#346]: https://github.com/uber-go/zap/pull/346 +[#365]: https://github.com/uber-go/zap/pull/365 +[#372]: https://github.com/uber-go/zap/pull/372 +[#385]: https://github.com/uber-go/zap/pull/385 +[#396]: https://github.com/uber-go/zap/pull/396 +[#386]: https://github.com/uber-go/zap/pull/386 +[#402]: https://github.com/uber-go/zap/pull/402 +[#415]: https://github.com/uber-go/zap/pull/415 +[#416]: https://github.com/uber-go/zap/pull/416 +[#424]: https://github.com/uber-go/zap/pull/424 +[#425]: https://github.com/uber-go/zap/pull/425 +[#431]: https://github.com/uber-go/zap/pull/431 +[#435]: https://github.com/uber-go/zap/pull/435 +[#444]: https://github.com/uber-go/zap/pull/444 +[#477]: https://github.com/uber-go/zap/pull/477 +[#465]: https://github.com/uber-go/zap/pull/465 +[#460]: https://github.com/uber-go/zap/pull/460 +[#470]: https://github.com/uber-go/zap/pull/470 +[#487]: https://github.com/uber-go/zap/pull/487 +[#490]: https://github.com/uber-go/zap/pull/490 +[#491]: https://github.com/uber-go/zap/pull/491 +[#504]: https://github.com/uber-go/zap/pull/504 +[#508]: https://github.com/uber-go/zap/pull/508 +[#518]: https://github.com/uber-go/zap/pull/518 +[#577]: https://github.com/uber-go/zap/pull/577 +[#574]: https://github.com/uber-go/zap/pull/574 +[#602]: https://github.com/uber-go/zap/pull/602 +[#572]: https://github.com/uber-go/zap/pull/572 +[#606]: https://github.com/uber-go/zap/pull/606 +[#614]: https://github.com/uber-go/zap/pull/614 +[#657]: https://github.com/uber-go/zap/pull/657 +[#706]: https://github.com/uber-go/zap/pull/706 +[#610]: https://github.com/uber-go/zap/pull/610 +[#675]: https://github.com/uber-go/zap/pull/675 +[#704]: https://github.com/uber-go/zap/pull/704 +[#725]: https://github.com/uber-go/zap/pull/725 +[#736]: https://github.com/uber-go/zap/pull/736 +[#751]: https://github.com/uber-go/zap/pull/751 +[#758]: https://github.com/uber-go/zap/pull/758 +[#771]: https://github.com/uber-go/zap/pull/771 +[#773]: https://github.com/uber-go/zap/pull/773 +[#775]: https://github.com/uber-go/zap/pull/775 +[#786]: https://github.com/uber-go/zap/pull/786 +[#791]: https://github.com/uber-go/zap/pull/791 +[#795]: https://github.com/uber-go/zap/pull/795 +[#799]: https://github.com/uber-go/zap/pull/799 +[#804]: https://github.com/uber-go/zap/pull/804 +[#812]: https://github.com/uber-go/zap/pull/812 +[#806]: https://github.com/uber-go/zap/pull/806 +[#813]: https://github.com/uber-go/zap/pull/813 diff --git a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..e327d9a --- /dev/null +++ b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, +body size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual +identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an +appointed representative at an online or offline event. Representation of a +project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at oss-conduct@uber.com. The project +team will review and investigate all complaints, and will respond in a way +that it deems appropriate to the circumstances. The project team is obligated +to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +[http://contributor-covenant.org/version/1/4][version]. + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/go.uber.org/zap/CONTRIBUTING.md b/vendor/go.uber.org/zap/CONTRIBUTING.md new file mode 100644 index 0000000..9454bba --- /dev/null +++ b/vendor/go.uber.org/zap/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contributing + +We'd love your help making zap the very best structured logging library in Go! + +If you'd like to add new exported APIs, please [open an issue][open-issue] +describing your proposal — discussing API changes ahead of time makes +pull request review much smoother. In your issue, pull request, and any other +communications, please remember to treat your fellow contributors with +respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously. + +Note that you'll need to sign [Uber's Contributor License Agreement][cla] +before we can accept any of your contributions. If necessary, a bot will remind +you to accept the CLA when you open your pull request. + +## Setup + +[Fork][fork], then clone the repository: + +``` +mkdir -p $GOPATH/src/go.uber.org +cd $GOPATH/src/go.uber.org +git clone git@github.com:your_github_username/zap.git +cd zap +git remote add upstream https://github.com/uber-go/zap.git +git fetch upstream +``` + +Install zap's dependencies: + +``` +make dependencies +``` + +Make sure that the tests and the linters pass: + +``` +make test +make lint +``` + +If you're not using the minor version of Go specified in the Makefile's +`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is +fine, but it means that you'll only discover lint failures after you open your +pull request. + +## Making Changes + +Start by creating a new branch for your changes: + +``` +cd $GOPATH/src/go.uber.org/zap +git checkout master +git fetch upstream +git rebase upstream/master +git checkout -b cool_new_feature +``` + +Make your changes, then ensure that `make lint` and `make test` still pass. If +you're satisfied with your changes, push them to your fork. + +``` +git push origin cool_new_feature +``` + +Then use the GitHub UI to open a pull request. + +At this point, you're waiting on us to review your changes. We *try* to respond +to issues and pull requests within a few business days, and we may suggest some +improvements or alternatives. Once your changes are approved, one of the +project maintainers will merge them. + +We're much more likely to approve your changes if you: + +* Add tests for new functionality. +* Write a [good commit message][commit-message]. +* Maintain backward compatibility. + +[fork]: https://github.com/uber-go/zap/fork +[open-issue]: https://github.com/uber-go/zap/issues/new +[cla]: https://cla-assistant.io/uber-go/zap +[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/vendor/go.uber.org/zap/FAQ.md b/vendor/go.uber.org/zap/FAQ.md new file mode 100644 index 0000000..4256d35 --- /dev/null +++ b/vendor/go.uber.org/zap/FAQ.md @@ -0,0 +1,155 @@ +# Frequently Asked Questions + +## Design + +### Why spend so much effort on logger performance? + +Of course, most applications won't notice the impact of a slow logger: they +already take tens or hundreds of milliseconds for each operation, so an extra +millisecond doesn't matter. + +On the other hand, why *not* make structured logging fast? The `SugaredLogger` +isn't any harder to use than other logging packages, and the `Logger` makes +structured logging possible in performance-sensitive contexts. Across a fleet +of Go microservices, making each application even slightly more efficient adds +up quickly. + +### Why aren't `Logger` and `SugaredLogger` interfaces? + +Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and +`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points +out][go-proverbs], "The bigger the interface, the weaker the abstraction." +Interfaces are also rigid — *any* change requires releasing a new major +version, since it breaks all third-party implementations. + +Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much +abstraction, and it lets us add methods without introducing breaking changes. +Your applications should define and depend upon an interface that includes +just the methods you use. + +### Why sample application logs? + +Applications often experience runs of errors, either because of a bug or +because of a misbehaving user. Logging errors is usually a good idea, but it +can easily make this bad situation worse: not only is your application coping +with a flood of errors, it's also spending extra CPU cycles and I/O logging +those errors. Since writes are typically serialized, logging limits throughput +when you need it most. + +Sampling fixes this problem by dropping repetitive log entries. Under normal +conditions, your application writes out every entry. When similar entries are +logged hundreds or thousands of times each second, though, zap begins dropping +duplicates to preserve throughput. + +### Why do the structured logging APIs take a message in addition to fields? + +Subjectively, we find it helpful to accompany structured context with a brief +description. This isn't critical during development, but it makes debugging +and operating unfamiliar systems much easier. + +More concretely, zap's sampling algorithm uses the message to identify +duplicate entries. In our experience, this is a practical middle ground +between random sampling (which often drops the exact entry that you need while +debugging) and hashing the complete entry (which is prohibitively expensive). + +### Why include package-global loggers? + +Since so many other logging packages include a global logger, many +applications aren't designed to accept loggers as explicit parameters. +Changing function signatures is often a breaking change, so zap includes +global loggers to simplify migration. + +Avoid them where possible. + +### Why include dedicated Panic and Fatal log levels? + +In general, application code should handle errors gracefully instead of using +`panic` or `os.Exit`. However, every rule has exceptions, and it's common to +crash when an error is truly unrecoverable. To avoid losing any information +— especially the reason for the crash — the logger must flush any +buffered entries before the process exits. + +Zap makes this easy by offering `Panic` and `Fatal` logging methods that +automatically flush before exiting. Of course, this doesn't guarantee that +logs will never be lost, but it eliminates a common error. + +See the discussion in uber-go/zap#207 for more details. + +### What's `DPanic`? + +`DPanic` stands for "panic in development." In development, it logs at +`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to +catch errors that are theoretically possible, but shouldn't actually happen, +*without* crashing in production. + +If you've ever written code like this, you need `DPanic`: + +```go +if err != nil { + panic(fmt.Sprintf("shouldn't ever get here: %v", err)) +} +``` + +## Installation + +### What does the error `expects import "go.uber.org/zap"` mean? + +Either zap was installed incorrectly or you're referencing the wrong package +name in your code. + +Zap's source code happens to be hosted on GitHub, but the [import +path][import-path] is `go.uber.org/zap`. This gives us, the project +maintainers, the freedom to move the source code if necessary. However, it +means that you need to take a little care when installing and using the +package. + +If you follow two simple rules, everything should work: install zap with `go +get -u go.uber.org/zap`, and always import it in your code with `import +"go.uber.org/zap"`. Your code shouldn't contain *any* references to +`github.com/uber-go/zap`. + +## Usage + +### Does zap support log rotation? + +Zap doesn't natively support rotating log files, since we prefer to leave this +to an external program like `logrotate`. + +However, it's easy to integrate a log rotation package like +[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`. + +```go +// lumberjack.Logger is already safe for concurrent use, so we don't need to +// lock it. +w := zapcore.AddSync(&lumberjack.Logger{ + Filename: "/var/log/myapp/foo.log", + MaxSize: 500, // megabytes + MaxBackups: 3, + MaxAge: 28, // days +}) +core := zapcore.NewCore( + zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), + w, + zap.InfoLevel, +) +logger := zap.New(core) +``` + +## Extensions + +We'd love to support every logging need within zap itself, but we're only +familiar with a handful of log ingestion systems, flag-parsing packages, and +the like. Rather than merging code that we can't effectively debug and +support, we'd rather grow an ecosystem of zap extensions. + +We're aware of the following extensions, but haven't used them ourselves: + +| Package | Integration | +| --- | --- | +| `github.com/tchap/zapext` | Sentry, syslog | +| `github.com/fgrosse/zaptest` | Ginkgo | +| `github.com/blendle/zapdriver` | Stackdriver | + +[go-proverbs]: https://go-proverbs.github.io/ +[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths +[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2 diff --git a/vendor/go.uber.org/zap/LICENSE.txt b/vendor/go.uber.org/zap/LICENSE.txt new file mode 100644 index 0000000..6652bed --- /dev/null +++ b/vendor/go.uber.org/zap/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016-2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile new file mode 100644 index 0000000..dfaf640 --- /dev/null +++ b/vendor/go.uber.org/zap/Makefile @@ -0,0 +1,63 @@ +export GOBIN ?= $(shell pwd)/bin + +GOLINT = $(GOBIN)/golint +STATICCHECK = $(GOBIN)/staticcheck +BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem + +# Directories containing independent Go modules. +# +# We track coverage only for the main module. +MODULE_DIRS = . ./benchmarks + +# Many Go tools take file globs or directories as arguments instead of packages. +GO_FILES := $(shell \ + find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ + -o -name '*.go' -print | cut -b3-) + +.PHONY: all +all: lint test + +.PHONY: lint +lint: $(GOLINT) $(STATICCHECK) + @rm -rf lint.log + @echo "Checking formatting..." + @gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log + @echo "Checking vet..." + @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log + @echo "Checking lint..." + @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log + @echo "Checking staticcheck..." + @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log + @echo "Checking for unresolved FIXMEs..." + @git grep -i fixme | grep -v -e Makefile | tee -a lint.log + @echo "Checking for license headers..." + @./checklicense.sh | tee -a lint.log + @[ ! -s lint.log ] + +$(GOLINT): + go install golang.org/x/lint/golint + +$(STATICCHECK): + go install honnef.co/go/tools/cmd/staticcheck + +.PHONY: test +test: + @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go test -race ./...) &&) true + +.PHONY: cover +cover: + go test -race -coverprofile=cover.out -coverpkg=./... ./... + go tool cover -html=cover.out -o cover.html + +.PHONY: bench +BENCH ?= . +bench: + @$(foreach dir,$(MODULE_DIRS), ( \ + cd $(dir) && \ + go list ./... | xargs -n1 go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) \ + ) &&) true + +.PHONY: updatereadme +updatereadme: + rm -f README.md + cat .readme.tmpl | go run internal/readme/readme.go > README.md diff --git a/vendor/go.uber.org/zap/README.md b/vendor/go.uber.org/zap/README.md new file mode 100644 index 0000000..bcea28a --- /dev/null +++ b/vendor/go.uber.org/zap/README.md @@ -0,0 +1,134 @@ +# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +Blazing fast, structured, leveled logging in Go. + +## Installation + +`go get -u go.uber.org/zap` + +Note that zap only supports the two most recent minor versions of Go. + +## Quick Start + +In contexts where performance is nice, but not critical, use the +`SugaredLogger`. It's 4-10x faster than other structured logging +packages and includes both structured and `printf`-style APIs. + +```go +logger, _ := zap.NewProduction() +defer logger.Sync() // flushes buffer, if any +sugar := logger.Sugar() +sugar.Infow("failed to fetch URL", + // Structured context as loosely typed key-value pairs. + "url", url, + "attempt", 3, + "backoff", time.Second, +) +sugar.Infof("Failed to fetch URL: %s", url) +``` + +When performance and type safety are critical, use the `Logger`. It's even +faster than the `SugaredLogger` and allocates far less, but it only supports +structured logging. + +```go +logger, _ := zap.NewProduction() +defer logger.Sync() +logger.Info("failed to fetch URL", + // Structured context as strongly typed Field values. + zap.String("url", url), + zap.Int("attempt", 3), + zap.Duration("backoff", time.Second), +) +``` + +See the [documentation][doc] and [FAQ](FAQ.md) for more details. + +## Performance + +For applications that log in the hot path, reflection-based serialization and +string formatting are prohibitively expensive — they're CPU-intensive +and make many small allocations. Put differently, using `encoding/json` and +`fmt.Fprintf` to log tons of `interface{}`s makes your application slow. + +Zap takes a different approach. It includes a reflection-free, zero-allocation +JSON encoder, and the base `Logger` strives to avoid serialization overhead +and allocations wherever possible. By building the high-level `SugaredLogger` +on that foundation, zap lets users *choose* when they need to count every +allocation and when they'd prefer a more familiar, loosely typed API. + +As measured by its own [benchmarking suite][], not only is zap more performant +than comparable structured logging packages — it's also faster than the +standard library. Like all benchmarks, take these with a grain of salt.[1](#footnote-versions) + +Log a message and 10 fields: + +| Package | Time | Time % to zap | Objects Allocated | +| :------ | :--: | :-----------: | :---------------: | +| :zap: zap | 862 ns/op | +0% | 5 allocs/op +| :zap: zap (sugared) | 1250 ns/op | +45% | 11 allocs/op +| zerolog | 4021 ns/op | +366% | 76 allocs/op +| go-kit | 4542 ns/op | +427% | 105 allocs/op +| apex/log | 26785 ns/op | +3007% | 115 allocs/op +| logrus | 29501 ns/op | +3322% | 125 allocs/op +| log15 | 29906 ns/op | +3369% | 122 allocs/op + +Log a message with a logger that already has 10 fields of context: + +| Package | Time | Time % to zap | Objects Allocated | +| :------ | :--: | :-----------: | :---------------: | +| :zap: zap | 126 ns/op | +0% | 0 allocs/op +| :zap: zap (sugared) | 187 ns/op | +48% | 2 allocs/op +| zerolog | 88 ns/op | -30% | 0 allocs/op +| go-kit | 5087 ns/op | +3937% | 103 allocs/op +| log15 | 18548 ns/op | +14621% | 73 allocs/op +| apex/log | 26012 ns/op | +20544% | 104 allocs/op +| logrus | 27236 ns/op | +21516% | 113 allocs/op + +Log a static string, without any context or `printf`-style templating: + +| Package | Time | Time % to zap | Objects Allocated | +| :------ | :--: | :-----------: | :---------------: | +| :zap: zap | 118 ns/op | +0% | 0 allocs/op +| :zap: zap (sugared) | 191 ns/op | +62% | 2 allocs/op +| zerolog | 93 ns/op | -21% | 0 allocs/op +| go-kit | 280 ns/op | +137% | 11 allocs/op +| standard library | 499 ns/op | +323% | 2 allocs/op +| apex/log | 1990 ns/op | +1586% | 10 allocs/op +| logrus | 3129 ns/op | +2552% | 24 allocs/op +| log15 | 3887 ns/op | +3194% | 23 allocs/op + +## Development Status: Stable + +All APIs are finalized, and no breaking changes will be made in the 1.x series +of releases. Users of semver-aware dependency management systems should pin +zap to `^1`. + +## Contributing + +We encourage and support an active, healthy community of contributors — +including you! Details are in the [contribution guide](CONTRIBUTING.md) and +the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on +issues and pull requests, but you can also report any negative conduct to +oss-conduct@uber.com. That email list is a private, safe space; even the zap +maintainers don't have access, so don't hesitate to hold us to a high +standard. + +
+ +Released under the [MIT License](LICENSE.txt). + +1 In particular, keep in mind that we may be +benchmarking against slightly older versions of other packages. Versions are +pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions) + +[doc-img]: https://godoc.org/go.uber.org/zap?status.svg +[doc]: https://godoc.org/go.uber.org/zap +[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master +[ci]: https://travis-ci.com/uber-go/zap +[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/uber-go/zap +[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks +[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod + diff --git a/vendor/go.uber.org/zap/array.go b/vendor/go.uber.org/zap/array.go new file mode 100644 index 0000000..5be3704 --- /dev/null +++ b/vendor/go.uber.org/zap/array.go @@ -0,0 +1,320 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "time" + + "go.uber.org/zap/zapcore" +) + +// Array constructs a field with the given key and ArrayMarshaler. It provides +// a flexible, but still type-safe and efficient, way to add array-like types +// to the logging context. The struct's MarshalLogArray method is called lazily. +func Array(key string, val zapcore.ArrayMarshaler) Field { + return Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val} +} + +// Bools constructs a field that carries a slice of bools. +func Bools(key string, bs []bool) Field { + return Array(key, bools(bs)) +} + +// ByteStrings constructs a field that carries a slice of []byte, each of which +// must be UTF-8 encoded text. +func ByteStrings(key string, bss [][]byte) Field { + return Array(key, byteStringsArray(bss)) +} + +// Complex128s constructs a field that carries a slice of complex numbers. +func Complex128s(key string, nums []complex128) Field { + return Array(key, complex128s(nums)) +} + +// Complex64s constructs a field that carries a slice of complex numbers. +func Complex64s(key string, nums []complex64) Field { + return Array(key, complex64s(nums)) +} + +// Durations constructs a field that carries a slice of time.Durations. +func Durations(key string, ds []time.Duration) Field { + return Array(key, durations(ds)) +} + +// Float64s constructs a field that carries a slice of floats. +func Float64s(key string, nums []float64) Field { + return Array(key, float64s(nums)) +} + +// Float32s constructs a field that carries a slice of floats. +func Float32s(key string, nums []float32) Field { + return Array(key, float32s(nums)) +} + +// Ints constructs a field that carries a slice of integers. +func Ints(key string, nums []int) Field { + return Array(key, ints(nums)) +} + +// Int64s constructs a field that carries a slice of integers. +func Int64s(key string, nums []int64) Field { + return Array(key, int64s(nums)) +} + +// Int32s constructs a field that carries a slice of integers. +func Int32s(key string, nums []int32) Field { + return Array(key, int32s(nums)) +} + +// Int16s constructs a field that carries a slice of integers. +func Int16s(key string, nums []int16) Field { + return Array(key, int16s(nums)) +} + +// Int8s constructs a field that carries a slice of integers. +func Int8s(key string, nums []int8) Field { + return Array(key, int8s(nums)) +} + +// Strings constructs a field that carries a slice of strings. +func Strings(key string, ss []string) Field { + return Array(key, stringArray(ss)) +} + +// Times constructs a field that carries a slice of time.Times. +func Times(key string, ts []time.Time) Field { + return Array(key, times(ts)) +} + +// Uints constructs a field that carries a slice of unsigned integers. +func Uints(key string, nums []uint) Field { + return Array(key, uints(nums)) +} + +// Uint64s constructs a field that carries a slice of unsigned integers. +func Uint64s(key string, nums []uint64) Field { + return Array(key, uint64s(nums)) +} + +// Uint32s constructs a field that carries a slice of unsigned integers. +func Uint32s(key string, nums []uint32) Field { + return Array(key, uint32s(nums)) +} + +// Uint16s constructs a field that carries a slice of unsigned integers. +func Uint16s(key string, nums []uint16) Field { + return Array(key, uint16s(nums)) +} + +// Uint8s constructs a field that carries a slice of unsigned integers. +func Uint8s(key string, nums []uint8) Field { + return Array(key, uint8s(nums)) +} + +// Uintptrs constructs a field that carries a slice of pointer addresses. +func Uintptrs(key string, us []uintptr) Field { + return Array(key, uintptrs(us)) +} + +// Errors constructs a field that carries a slice of errors. +func Errors(key string, errs []error) Field { + return Array(key, errArray(errs)) +} + +type bools []bool + +func (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range bs { + arr.AppendBool(bs[i]) + } + return nil +} + +type byteStringsArray [][]byte + +func (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range bss { + arr.AppendByteString(bss[i]) + } + return nil +} + +type complex128s []complex128 + +func (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendComplex128(nums[i]) + } + return nil +} + +type complex64s []complex64 + +func (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendComplex64(nums[i]) + } + return nil +} + +type durations []time.Duration + +func (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range ds { + arr.AppendDuration(ds[i]) + } + return nil +} + +type float64s []float64 + +func (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendFloat64(nums[i]) + } + return nil +} + +type float32s []float32 + +func (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendFloat32(nums[i]) + } + return nil +} + +type ints []int + +func (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt(nums[i]) + } + return nil +} + +type int64s []int64 + +func (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt64(nums[i]) + } + return nil +} + +type int32s []int32 + +func (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt32(nums[i]) + } + return nil +} + +type int16s []int16 + +func (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt16(nums[i]) + } + return nil +} + +type int8s []int8 + +func (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt8(nums[i]) + } + return nil +} + +type stringArray []string + +func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range ss { + arr.AppendString(ss[i]) + } + return nil +} + +type times []time.Time + +func (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range ts { + arr.AppendTime(ts[i]) + } + return nil +} + +type uints []uint + +func (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint(nums[i]) + } + return nil +} + +type uint64s []uint64 + +func (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint64(nums[i]) + } + return nil +} + +type uint32s []uint32 + +func (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint32(nums[i]) + } + return nil +} + +type uint16s []uint16 + +func (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint16(nums[i]) + } + return nil +} + +type uint8s []uint8 + +func (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint8(nums[i]) + } + return nil +} + +type uintptrs []uintptr + +func (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUintptr(nums[i]) + } + return nil +} diff --git a/vendor/go.uber.org/zap/buffer/buffer.go b/vendor/go.uber.org/zap/buffer/buffer.go new file mode 100644 index 0000000..3f4b86e --- /dev/null +++ b/vendor/go.uber.org/zap/buffer/buffer.go @@ -0,0 +1,123 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package buffer provides a thin wrapper around a byte slice. Unlike the +// standard library's bytes.Buffer, it supports a portion of the strconv +// package's zero-allocation formatters. +package buffer // import "go.uber.org/zap/buffer" + +import ( + "strconv" + "time" +) + +const _size = 1024 // by default, create 1 KiB buffers + +// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so +// the only way to construct one is via a Pool. +type Buffer struct { + bs []byte + pool Pool +} + +// AppendByte writes a single byte to the Buffer. +func (b *Buffer) AppendByte(v byte) { + b.bs = append(b.bs, v) +} + +// AppendString writes a string to the Buffer. +func (b *Buffer) AppendString(s string) { + b.bs = append(b.bs, s...) +} + +// AppendInt appends an integer to the underlying buffer (assuming base 10). +func (b *Buffer) AppendInt(i int64) { + b.bs = strconv.AppendInt(b.bs, i, 10) +} + +// AppendTime appends the time formatted using the specified layout. +func (b *Buffer) AppendTime(t time.Time, layout string) { + b.bs = t.AppendFormat(b.bs, layout) +} + +// AppendUint appends an unsigned integer to the underlying buffer (assuming +// base 10). +func (b *Buffer) AppendUint(i uint64) { + b.bs = strconv.AppendUint(b.bs, i, 10) +} + +// AppendBool appends a bool to the underlying buffer. +func (b *Buffer) AppendBool(v bool) { + b.bs = strconv.AppendBool(b.bs, v) +} + +// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN +// or +/- Inf. +func (b *Buffer) AppendFloat(f float64, bitSize int) { + b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize) +} + +// Len returns the length of the underlying byte slice. +func (b *Buffer) Len() int { + return len(b.bs) +} + +// Cap returns the capacity of the underlying byte slice. +func (b *Buffer) Cap() int { + return cap(b.bs) +} + +// Bytes returns a mutable reference to the underlying byte slice. +func (b *Buffer) Bytes() []byte { + return b.bs +} + +// String returns a string copy of the underlying byte slice. +func (b *Buffer) String() string { + return string(b.bs) +} + +// Reset resets the underlying byte slice. Subsequent writes re-use the slice's +// backing array. +func (b *Buffer) Reset() { + b.bs = b.bs[:0] +} + +// Write implements io.Writer. +func (b *Buffer) Write(bs []byte) (int, error) { + b.bs = append(b.bs, bs...) + return len(bs), nil +} + +// TrimNewline trims any final "\n" byte from the end of the buffer. +func (b *Buffer) TrimNewline() { + if i := len(b.bs) - 1; i >= 0 { + if b.bs[i] == '\n' { + b.bs = b.bs[:i] + } + } +} + +// Free returns the Buffer to its Pool. +// +// Callers must not retain references to the Buffer after calling Free. +func (b *Buffer) Free() { + b.pool.put(b) +} diff --git a/vendor/go.uber.org/zap/buffer/pool.go b/vendor/go.uber.org/zap/buffer/pool.go new file mode 100644 index 0000000..8fb3e20 --- /dev/null +++ b/vendor/go.uber.org/zap/buffer/pool.go @@ -0,0 +1,49 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package buffer + +import "sync" + +// A Pool is a type-safe wrapper around a sync.Pool. +type Pool struct { + p *sync.Pool +} + +// NewPool constructs a new Pool. +func NewPool() Pool { + return Pool{p: &sync.Pool{ + New: func() interface{} { + return &Buffer{bs: make([]byte, 0, _size)} + }, + }} +} + +// Get retrieves a Buffer from the pool, creating one if necessary. +func (p Pool) Get() *Buffer { + buf := p.p.Get().(*Buffer) + buf.Reset() + buf.pool = p + return buf +} + +func (p Pool) put(buf *Buffer) { + p.p.Put(buf) +} diff --git a/vendor/go.uber.org/zap/checklicense.sh b/vendor/go.uber.org/zap/checklicense.sh new file mode 100644 index 0000000..345ac8b --- /dev/null +++ b/vendor/go.uber.org/zap/checklicense.sh @@ -0,0 +1,17 @@ +#!/bin/bash -e + +ERROR_COUNT=0 +while read -r file +do + case "$(head -1 "${file}")" in + *"Copyright (c) "*" Uber Technologies, Inc.") + # everything's cool + ;; + *) + echo "$file is missing license header." + (( ERROR_COUNT++ )) + ;; + esac +done < <(git ls-files "*\.go") + +exit $ERROR_COUNT diff --git a/vendor/go.uber.org/zap/config.go b/vendor/go.uber.org/zap/config.go new file mode 100644 index 0000000..192fd1a --- /dev/null +++ b/vendor/go.uber.org/zap/config.go @@ -0,0 +1,262 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + "sort" + "time" + + "go.uber.org/zap/zapcore" +) + +// SamplingConfig sets a sampling strategy for the logger. Sampling caps the +// global CPU and I/O load that logging puts on your process while attempting +// to preserve a representative subset of your logs. +// +// If specified, the Sampler will invoke the Hook after each decision. +// +// Values configured here are per-second. See zapcore.NewSamplerWithOptions for +// details. +type SamplingConfig struct { + Initial int `json:"initial" yaml:"initial"` + Thereafter int `json:"thereafter" yaml:"thereafter"` + Hook func(zapcore.Entry, zapcore.SamplingDecision) `json:"-" yaml:"-"` +} + +// Config offers a declarative way to construct a logger. It doesn't do +// anything that can't be done with New, Options, and the various +// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to +// toggle common options. +// +// Note that Config intentionally supports only the most common options. More +// unusual logging setups (logging to network connections or message queues, +// splitting output between multiple files, etc.) are possible, but require +// direct use of the zapcore package. For sample code, see the package-level +// BasicConfiguration and AdvancedConfiguration examples. +// +// For an example showing runtime log level changes, see the documentation for +// AtomicLevel. +type Config struct { + // Level is the minimum enabled logging level. Note that this is a dynamic + // level, so calling Config.Level.SetLevel will atomically change the log + // level of all loggers descended from this config. + Level AtomicLevel `json:"level" yaml:"level"` + // Development puts the logger in development mode, which changes the + // behavior of DPanicLevel and takes stacktraces more liberally. + Development bool `json:"development" yaml:"development"` + // DisableCaller stops annotating logs with the calling function's file + // name and line number. By default, all logs are annotated. + DisableCaller bool `json:"disableCaller" yaml:"disableCaller"` + // DisableStacktrace completely disables automatic stacktrace capturing. By + // default, stacktraces are captured for WarnLevel and above logs in + // development and ErrorLevel and above in production. + DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"` + // Sampling sets a sampling policy. A nil SamplingConfig disables sampling. + Sampling *SamplingConfig `json:"sampling" yaml:"sampling"` + // Encoding sets the logger's encoding. Valid values are "json" and + // "console", as well as any third-party encodings registered via + // RegisterEncoder. + Encoding string `json:"encoding" yaml:"encoding"` + // EncoderConfig sets options for the chosen encoder. See + // zapcore.EncoderConfig for details. + EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"` + // OutputPaths is a list of URLs or file paths to write logging output to. + // See Open for details. + OutputPaths []string `json:"outputPaths" yaml:"outputPaths"` + // ErrorOutputPaths is a list of URLs to write internal logger errors to. + // The default is standard error. + // + // Note that this setting only affects internal errors; for sample code that + // sends error-level logs to a different location from info- and debug-level + // logs, see the package-level AdvancedConfiguration example. + ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"` + // InitialFields is a collection of fields to add to the root logger. + InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"` +} + +// NewProductionEncoderConfig returns an opinionated EncoderConfig for +// production environments. +func NewProductionEncoderConfig() zapcore.EncoderConfig { + return zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "level", + NameKey: "logger", + CallerKey: "caller", + MessageKey: "msg", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.EpochTimeEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } +} + +// NewProductionConfig is a reasonable production logging configuration. +// Logging is enabled at InfoLevel and above. +// +// It uses a JSON encoder, writes to standard error, and enables sampling. +// Stacktraces are automatically included on logs of ErrorLevel and above. +func NewProductionConfig() Config { + return Config{ + Level: NewAtomicLevelAt(InfoLevel), + Development: false, + Sampling: &SamplingConfig{ + Initial: 100, + Thereafter: 100, + }, + Encoding: "json", + EncoderConfig: NewProductionEncoderConfig(), + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } +} + +// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for +// development environments. +func NewDevelopmentEncoderConfig() zapcore.EncoderConfig { + return zapcore.EncoderConfig{ + // Keys can be anything except the empty string. + TimeKey: "T", + LevelKey: "L", + NameKey: "N", + CallerKey: "C", + MessageKey: "M", + StacktraceKey: "S", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.CapitalLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeDuration: zapcore.StringDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } +} + +// NewDevelopmentConfig is a reasonable development logging configuration. +// Logging is enabled at DebugLevel and above. +// +// It enables development mode (which makes DPanicLevel logs panic), uses a +// console encoder, writes to standard error, and disables sampling. +// Stacktraces are automatically included on logs of WarnLevel and above. +func NewDevelopmentConfig() Config { + return Config{ + Level: NewAtomicLevelAt(DebugLevel), + Development: true, + Encoding: "console", + EncoderConfig: NewDevelopmentEncoderConfig(), + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } +} + +// Build constructs a logger from the Config and Options. +func (cfg Config) Build(opts ...Option) (*Logger, error) { + enc, err := cfg.buildEncoder() + if err != nil { + return nil, err + } + + sink, errSink, err := cfg.openSinks() + if err != nil { + return nil, err + } + + if cfg.Level == (AtomicLevel{}) { + return nil, fmt.Errorf("missing Level") + } + + log := New( + zapcore.NewCore(enc, sink, cfg.Level), + cfg.buildOptions(errSink)..., + ) + if len(opts) > 0 { + log = log.WithOptions(opts...) + } + return log, nil +} + +func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option { + opts := []Option{ErrorOutput(errSink)} + + if cfg.Development { + opts = append(opts, Development()) + } + + if !cfg.DisableCaller { + opts = append(opts, AddCaller()) + } + + stackLevel := ErrorLevel + if cfg.Development { + stackLevel = WarnLevel + } + if !cfg.DisableStacktrace { + opts = append(opts, AddStacktrace(stackLevel)) + } + + if scfg := cfg.Sampling; scfg != nil { + opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core { + var samplerOpts []zapcore.SamplerOption + if scfg.Hook != nil { + samplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook)) + } + return zapcore.NewSamplerWithOptions( + core, + time.Second, + cfg.Sampling.Initial, + cfg.Sampling.Thereafter, + samplerOpts..., + ) + })) + } + + if len(cfg.InitialFields) > 0 { + fs := make([]Field, 0, len(cfg.InitialFields)) + keys := make([]string, 0, len(cfg.InitialFields)) + for k := range cfg.InitialFields { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + fs = append(fs, Any(k, cfg.InitialFields[k])) + } + opts = append(opts, Fields(fs...)) + } + + return opts +} + +func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) { + sink, closeOut, err := Open(cfg.OutputPaths...) + if err != nil { + return nil, nil, err + } + errSink, _, err := Open(cfg.ErrorOutputPaths...) + if err != nil { + closeOut() + return nil, nil, err + } + return sink, errSink, nil +} + +func (cfg Config) buildEncoder() (zapcore.Encoder, error) { + return newEncoder(cfg.Encoding, cfg.EncoderConfig) +} diff --git a/vendor/go.uber.org/zap/doc.go b/vendor/go.uber.org/zap/doc.go new file mode 100644 index 0000000..8638dd1 --- /dev/null +++ b/vendor/go.uber.org/zap/doc.go @@ -0,0 +1,113 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package zap provides fast, structured, leveled logging. +// +// For applications that log in the hot path, reflection-based serialization +// and string formatting are prohibitively expensive - they're CPU-intensive +// and make many small allocations. Put differently, using json.Marshal and +// fmt.Fprintf to log tons of interface{} makes your application slow. +// +// Zap takes a different approach. It includes a reflection-free, +// zero-allocation JSON encoder, and the base Logger strives to avoid +// serialization overhead and allocations wherever possible. By building the +// high-level SugaredLogger on that foundation, zap lets users choose when +// they need to count every allocation and when they'd prefer a more familiar, +// loosely typed API. +// +// Choosing a Logger +// +// In contexts where performance is nice, but not critical, use the +// SugaredLogger. It's 4-10x faster than other structured logging packages and +// supports both structured and printf-style logging. Like log15 and go-kit, +// the SugaredLogger's structured logging APIs are loosely typed and accept a +// variadic number of key-value pairs. (For more advanced use cases, they also +// accept strongly typed fields - see the SugaredLogger.With documentation for +// details.) +// sugar := zap.NewExample().Sugar() +// defer sugar.Sync() +// sugar.Infow("failed to fetch URL", +// "url", "http://example.com", +// "attempt", 3, +// "backoff", time.Second, +// ) +// sugar.Infof("failed to fetch URL: %s", "http://example.com") +// +// By default, loggers are unbuffered. However, since zap's low-level APIs +// allow buffering, calling Sync before letting your process exit is a good +// habit. +// +// In the rare contexts where every microsecond and every allocation matter, +// use the Logger. It's even faster than the SugaredLogger and allocates far +// less, but it only supports strongly-typed, structured logging. +// logger := zap.NewExample() +// defer logger.Sync() +// logger.Info("failed to fetch URL", +// zap.String("url", "http://example.com"), +// zap.Int("attempt", 3), +// zap.Duration("backoff", time.Second), +// ) +// +// Choosing between the Logger and SugaredLogger doesn't need to be an +// application-wide decision: converting between the two is simple and +// inexpensive. +// logger := zap.NewExample() +// defer logger.Sync() +// sugar := logger.Sugar() +// plain := sugar.Desugar() +// +// Configuring Zap +// +// The simplest way to build a Logger is to use zap's opinionated presets: +// NewExample, NewProduction, and NewDevelopment. These presets build a logger +// with a single function call: +// logger, err := zap.NewProduction() +// if err != nil { +// log.Fatalf("can't initialize zap logger: %v", err) +// } +// defer logger.Sync() +// +// Presets are fine for small projects, but larger projects and organizations +// naturally require a bit more customization. For most users, zap's Config +// struct strikes the right balance between flexibility and convenience. See +// the package-level BasicConfiguration example for sample code. +// +// More unusual configurations (splitting output between files, sending logs +// to a message queue, etc.) are possible, but require direct use of +// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration +// example for sample code. +// +// Extending Zap +// +// The zap package itself is a relatively thin wrapper around the interfaces +// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g., +// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an +// exception aggregation service, like Sentry or Rollbar) typically requires +// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core +// interfaces. See the zapcore documentation for details. +// +// Similarly, package authors can use the high-performance Encoder and Core +// implementations in the zapcore package to build their own loggers. +// +// Frequently Asked Questions +// +// An FAQ covering everything from installation errors to design decisions is +// available at https://github.com/uber-go/zap/blob/master/FAQ.md. +package zap // import "go.uber.org/zap" diff --git a/vendor/go.uber.org/zap/encoder.go b/vendor/go.uber.org/zap/encoder.go new file mode 100644 index 0000000..08ed833 --- /dev/null +++ b/vendor/go.uber.org/zap/encoder.go @@ -0,0 +1,79 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "errors" + "fmt" + "sync" + + "go.uber.org/zap/zapcore" +) + +var ( + errNoEncoderNameSpecified = errors.New("no encoder name specified") + + _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){ + "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { + return zapcore.NewConsoleEncoder(encoderConfig), nil + }, + "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { + return zapcore.NewJSONEncoder(encoderConfig), nil + }, + } + _encoderMutex sync.RWMutex +) + +// RegisterEncoder registers an encoder constructor, which the Config struct +// can then reference. By default, the "json" and "console" encoders are +// registered. +// +// Attempting to register an encoder whose name is already taken returns an +// error. +func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error { + _encoderMutex.Lock() + defer _encoderMutex.Unlock() + if name == "" { + return errNoEncoderNameSpecified + } + if _, ok := _encoderNameToConstructor[name]; ok { + return fmt.Errorf("encoder already registered for name %q", name) + } + _encoderNameToConstructor[name] = constructor + return nil +} + +func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { + if encoderConfig.TimeKey != "" && encoderConfig.EncodeTime == nil { + return nil, fmt.Errorf("missing EncodeTime in EncoderConfig") + } + + _encoderMutex.RLock() + defer _encoderMutex.RUnlock() + if name == "" { + return nil, errNoEncoderNameSpecified + } + constructor, ok := _encoderNameToConstructor[name] + if !ok { + return nil, fmt.Errorf("no encoder registered for name %q", name) + } + return constructor(encoderConfig) +} diff --git a/vendor/go.uber.org/zap/error.go b/vendor/go.uber.org/zap/error.go new file mode 100644 index 0000000..65982a5 --- /dev/null +++ b/vendor/go.uber.org/zap/error.go @@ -0,0 +1,80 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "sync" + + "go.uber.org/zap/zapcore" +) + +var _errArrayElemPool = sync.Pool{New: func() interface{} { + return &errArrayElem{} +}} + +// Error is shorthand for the common idiom NamedError("error", err). +func Error(err error) Field { + return NamedError("error", err) +} + +// NamedError constructs a field that lazily stores err.Error() under the +// provided key. Errors which also implement fmt.Formatter (like those produced +// by github.com/pkg/errors) will also have their verbose representation stored +// under key+"Verbose". If passed a nil error, the field is a no-op. +// +// For the common case in which the key is simply "error", the Error function +// is shorter and less repetitive. +func NamedError(key string, err error) Field { + if err == nil { + return Skip() + } + return Field{Key: key, Type: zapcore.ErrorType, Interface: err} +} + +type errArray []error + +func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range errs { + if errs[i] == nil { + continue + } + // To represent each error as an object with an "error" attribute and + // potentially an "errorVerbose" attribute, we need to wrap it in a + // type that implements LogObjectMarshaler. To prevent this from + // allocating, pool the wrapper type. + elem := _errArrayElemPool.Get().(*errArrayElem) + elem.error = errs[i] + arr.AppendObject(elem) + elem.error = nil + _errArrayElemPool.Put(elem) + } + return nil +} + +type errArrayElem struct { + error +} + +func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error { + // Re-use the error field's logic, which supports non-standard error types. + Error(e.error).AddTo(enc) + return nil +} diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go new file mode 100644 index 0000000..dd558fc --- /dev/null +++ b/vendor/go.uber.org/zap/field.go @@ -0,0 +1,533 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + "math" + "time" + + "go.uber.org/zap/zapcore" +) + +// Field is an alias for Field. Aliasing this type dramatically +// improves the navigability of this package's API documentation. +type Field = zapcore.Field + +var ( + _minTimeInt64 = time.Unix(0, math.MinInt64) + _maxTimeInt64 = time.Unix(0, math.MaxInt64) +) + +// Skip constructs a no-op field, which is often useful when handling invalid +// inputs in other Field constructors. +func Skip() Field { + return Field{Type: zapcore.SkipType} +} + +// nilField returns a field which will marshal explicitly as nil. See motivation +// in https://github.com/uber-go/zap/issues/753 . If we ever make breaking +// changes and add zapcore.NilType and zapcore.ObjectEncoder.AddNil, the +// implementation here should be changed to reflect that. +func nilField(key string) Field { return Reflect(key, nil) } + +// Binary constructs a field that carries an opaque binary blob. +// +// Binary data is serialized in an encoding-appropriate format. For example, +// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text, +// use ByteString. +func Binary(key string, val []byte) Field { + return Field{Key: key, Type: zapcore.BinaryType, Interface: val} +} + +// Bool constructs a field that carries a bool. +func Bool(key string, val bool) Field { + var ival int64 + if val { + ival = 1 + } + return Field{Key: key, Type: zapcore.BoolType, Integer: ival} +} + +// Boolp constructs a field that carries a *bool. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Boolp(key string, val *bool) Field { + if val == nil { + return nilField(key) + } + return Bool(key, *val) +} + +// ByteString constructs a field that carries UTF-8 encoded text as a []byte. +// To log opaque binary blobs (which aren't necessarily valid UTF-8), use +// Binary. +func ByteString(key string, val []byte) Field { + return Field{Key: key, Type: zapcore.ByteStringType, Interface: val} +} + +// Complex128 constructs a field that carries a complex number. Unlike most +// numeric fields, this costs an allocation (to convert the complex128 to +// interface{}). +func Complex128(key string, val complex128) Field { + return Field{Key: key, Type: zapcore.Complex128Type, Interface: val} +} + +// Complex128p constructs a field that carries a *complex128. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Complex128p(key string, val *complex128) Field { + if val == nil { + return nilField(key) + } + return Complex128(key, *val) +} + +// Complex64 constructs a field that carries a complex number. Unlike most +// numeric fields, this costs an allocation (to convert the complex64 to +// interface{}). +func Complex64(key string, val complex64) Field { + return Field{Key: key, Type: zapcore.Complex64Type, Interface: val} +} + +// Complex64p constructs a field that carries a *complex64. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Complex64p(key string, val *complex64) Field { + if val == nil { + return nilField(key) + } + return Complex64(key, *val) +} + +// Float64 constructs a field that carries a float64. The way the +// floating-point value is represented is encoder-dependent, so marshaling is +// necessarily lazy. +func Float64(key string, val float64) Field { + return Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))} +} + +// Float64p constructs a field that carries a *float64. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Float64p(key string, val *float64) Field { + if val == nil { + return nilField(key) + } + return Float64(key, *val) +} + +// Float32 constructs a field that carries a float32. The way the +// floating-point value is represented is encoder-dependent, so marshaling is +// necessarily lazy. +func Float32(key string, val float32) Field { + return Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))} +} + +// Float32p constructs a field that carries a *float32. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Float32p(key string, val *float32) Field { + if val == nil { + return nilField(key) + } + return Float32(key, *val) +} + +// Int constructs a field with the given key and value. +func Int(key string, val int) Field { + return Int64(key, int64(val)) +} + +// Intp constructs a field that carries a *int. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Intp(key string, val *int) Field { + if val == nil { + return nilField(key) + } + return Int(key, *val) +} + +// Int64 constructs a field with the given key and value. +func Int64(key string, val int64) Field { + return Field{Key: key, Type: zapcore.Int64Type, Integer: val} +} + +// Int64p constructs a field that carries a *int64. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Int64p(key string, val *int64) Field { + if val == nil { + return nilField(key) + } + return Int64(key, *val) +} + +// Int32 constructs a field with the given key and value. +func Int32(key string, val int32) Field { + return Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)} +} + +// Int32p constructs a field that carries a *int32. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Int32p(key string, val *int32) Field { + if val == nil { + return nilField(key) + } + return Int32(key, *val) +} + +// Int16 constructs a field with the given key and value. +func Int16(key string, val int16) Field { + return Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)} +} + +// Int16p constructs a field that carries a *int16. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Int16p(key string, val *int16) Field { + if val == nil { + return nilField(key) + } + return Int16(key, *val) +} + +// Int8 constructs a field with the given key and value. +func Int8(key string, val int8) Field { + return Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)} +} + +// Int8p constructs a field that carries a *int8. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Int8p(key string, val *int8) Field { + if val == nil { + return nilField(key) + } + return Int8(key, *val) +} + +// String constructs a field with the given key and value. +func String(key string, val string) Field { + return Field{Key: key, Type: zapcore.StringType, String: val} +} + +// Stringp constructs a field that carries a *string. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Stringp(key string, val *string) Field { + if val == nil { + return nilField(key) + } + return String(key, *val) +} + +// Uint constructs a field with the given key and value. +func Uint(key string, val uint) Field { + return Uint64(key, uint64(val)) +} + +// Uintp constructs a field that carries a *uint. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Uintp(key string, val *uint) Field { + if val == nil { + return nilField(key) + } + return Uint(key, *val) +} + +// Uint64 constructs a field with the given key and value. +func Uint64(key string, val uint64) Field { + return Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)} +} + +// Uint64p constructs a field that carries a *uint64. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Uint64p(key string, val *uint64) Field { + if val == nil { + return nilField(key) + } + return Uint64(key, *val) +} + +// Uint32 constructs a field with the given key and value. +func Uint32(key string, val uint32) Field { + return Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)} +} + +// Uint32p constructs a field that carries a *uint32. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Uint32p(key string, val *uint32) Field { + if val == nil { + return nilField(key) + } + return Uint32(key, *val) +} + +// Uint16 constructs a field with the given key and value. +func Uint16(key string, val uint16) Field { + return Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)} +} + +// Uint16p constructs a field that carries a *uint16. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Uint16p(key string, val *uint16) Field { + if val == nil { + return nilField(key) + } + return Uint16(key, *val) +} + +// Uint8 constructs a field with the given key and value. +func Uint8(key string, val uint8) Field { + return Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)} +} + +// Uint8p constructs a field that carries a *uint8. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Uint8p(key string, val *uint8) Field { + if val == nil { + return nilField(key) + } + return Uint8(key, *val) +} + +// Uintptr constructs a field with the given key and value. +func Uintptr(key string, val uintptr) Field { + return Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)} +} + +// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Uintptrp(key string, val *uintptr) Field { + if val == nil { + return nilField(key) + } + return Uintptr(key, *val) +} + +// Reflect constructs a field with the given key and an arbitrary object. It uses +// an encoding-appropriate, reflection-based function to lazily serialize nearly +// any object into the logging context, but it's relatively slow and +// allocation-heavy. Outside tests, Any is always a better choice. +// +// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect +// includes the error message in the final log output. +func Reflect(key string, val interface{}) Field { + return Field{Key: key, Type: zapcore.ReflectType, Interface: val} +} + +// Namespace creates a named, isolated scope within the logger's context. All +// subsequent fields will be added to the new namespace. +// +// This helps prevent key collisions when injecting loggers into sub-components +// or third-party libraries. +func Namespace(key string) Field { + return Field{Key: key, Type: zapcore.NamespaceType} +} + +// Stringer constructs a field with the given key and the output of the value's +// String method. The Stringer's String method is called lazily. +func Stringer(key string, val fmt.Stringer) Field { + return Field{Key: key, Type: zapcore.StringerType, Interface: val} +} + +// Time constructs a Field with the given key and value. The encoder +// controls how the time is serialized. +func Time(key string, val time.Time) Field { + if val.Before(_minTimeInt64) || val.After(_maxTimeInt64) { + return Field{Key: key, Type: zapcore.TimeFullType, Interface: val} + } + return Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()} +} + +// Timep constructs a field that carries a *time.Time. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Timep(key string, val *time.Time) Field { + if val == nil { + return nilField(key) + } + return Time(key, *val) +} + +// Stack constructs a field that stores a stacktrace of the current goroutine +// under provided key. Keep in mind that taking a stacktrace is eager and +// expensive (relatively speaking); this function both makes an allocation and +// takes about two microseconds. +func Stack(key string) Field { + // Returning the stacktrace as a string costs an allocation, but saves us + // from expanding the zapcore.Field union struct to include a byte slice. Since + // taking a stacktrace is already so expensive (~10us), the extra allocation + // is okay. + return String(key, takeStacktrace()) +} + +// Duration constructs a field with the given key and value. The encoder +// controls how the duration is serialized. +func Duration(key string, val time.Duration) Field { + return Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)} +} + +// Durationp constructs a field that carries a *time.Duration. The returned Field will safely +// and explicitly represent `nil` when appropriate. +func Durationp(key string, val *time.Duration) Field { + if val == nil { + return nilField(key) + } + return Duration(key, *val) +} + +// Object constructs a field with the given key and ObjectMarshaler. It +// provides a flexible, but still type-safe and efficient, way to add map- or +// struct-like user-defined types to the logging context. The struct's +// MarshalLogObject method is called lazily. +func Object(key string, val zapcore.ObjectMarshaler) Field { + return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val} +} + +// Any takes a key and an arbitrary value and chooses the best way to represent +// them as a field, falling back to a reflection-based approach only if +// necessary. +// +// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between +// them. To minimize surprises, []byte values are treated as binary blobs, byte +// values are treated as uint8, and runes are always treated as integers. +func Any(key string, value interface{}) Field { + switch val := value.(type) { + case zapcore.ObjectMarshaler: + return Object(key, val) + case zapcore.ArrayMarshaler: + return Array(key, val) + case bool: + return Bool(key, val) + case *bool: + return Boolp(key, val) + case []bool: + return Bools(key, val) + case complex128: + return Complex128(key, val) + case *complex128: + return Complex128p(key, val) + case []complex128: + return Complex128s(key, val) + case complex64: + return Complex64(key, val) + case *complex64: + return Complex64p(key, val) + case []complex64: + return Complex64s(key, val) + case float64: + return Float64(key, val) + case *float64: + return Float64p(key, val) + case []float64: + return Float64s(key, val) + case float32: + return Float32(key, val) + case *float32: + return Float32p(key, val) + case []float32: + return Float32s(key, val) + case int: + return Int(key, val) + case *int: + return Intp(key, val) + case []int: + return Ints(key, val) + case int64: + return Int64(key, val) + case *int64: + return Int64p(key, val) + case []int64: + return Int64s(key, val) + case int32: + return Int32(key, val) + case *int32: + return Int32p(key, val) + case []int32: + return Int32s(key, val) + case int16: + return Int16(key, val) + case *int16: + return Int16p(key, val) + case []int16: + return Int16s(key, val) + case int8: + return Int8(key, val) + case *int8: + return Int8p(key, val) + case []int8: + return Int8s(key, val) + case string: + return String(key, val) + case *string: + return Stringp(key, val) + case []string: + return Strings(key, val) + case uint: + return Uint(key, val) + case *uint: + return Uintp(key, val) + case []uint: + return Uints(key, val) + case uint64: + return Uint64(key, val) + case *uint64: + return Uint64p(key, val) + case []uint64: + return Uint64s(key, val) + case uint32: + return Uint32(key, val) + case *uint32: + return Uint32p(key, val) + case []uint32: + return Uint32s(key, val) + case uint16: + return Uint16(key, val) + case *uint16: + return Uint16p(key, val) + case []uint16: + return Uint16s(key, val) + case uint8: + return Uint8(key, val) + case *uint8: + return Uint8p(key, val) + case []byte: + return Binary(key, val) + case uintptr: + return Uintptr(key, val) + case *uintptr: + return Uintptrp(key, val) + case []uintptr: + return Uintptrs(key, val) + case time.Time: + return Time(key, val) + case *time.Time: + return Timep(key, val) + case []time.Time: + return Times(key, val) + case time.Duration: + return Duration(key, val) + case *time.Duration: + return Durationp(key, val) + case []time.Duration: + return Durations(key, val) + case error: + return NamedError(key, val) + case []error: + return Errors(key, val) + case fmt.Stringer: + return Stringer(key, val) + default: + return Reflect(key, val) + } +} diff --git a/vendor/go.uber.org/zap/flag.go b/vendor/go.uber.org/zap/flag.go new file mode 100644 index 0000000..1312875 --- /dev/null +++ b/vendor/go.uber.org/zap/flag.go @@ -0,0 +1,39 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "flag" + + "go.uber.org/zap/zapcore" +) + +// LevelFlag uses the standard library's flag.Var to declare a global flag +// with the specified name, default, and usage guidance. The returned value is +// a pointer to the value of the flag. +// +// If you don't want to use the flag package's global state, you can use any +// non-nil *Level as a flag.Value with your own *flag.FlagSet. +func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level { + lvl := defaultLevel + flag.Var(&lvl, name, usage) + return &lvl +} diff --git a/vendor/go.uber.org/zap/glide.yaml b/vendor/go.uber.org/zap/glide.yaml new file mode 100644 index 0000000..8e1d05e --- /dev/null +++ b/vendor/go.uber.org/zap/glide.yaml @@ -0,0 +1,34 @@ +package: go.uber.org/zap +license: MIT +import: +- package: go.uber.org/atomic + version: ^1 +- package: go.uber.org/multierr + version: ^1 +testImport: +- package: github.com/satori/go.uuid +- package: github.com/sirupsen/logrus +- package: github.com/apex/log + subpackages: + - handlers/json +- package: github.com/go-kit/kit + subpackages: + - log +- package: github.com/stretchr/testify + subpackages: + - assert + - require +- package: gopkg.in/inconshreveable/log15.v2 +- package: github.com/mattn/goveralls +- package: github.com/pborman/uuid +- package: github.com/pkg/errors +- package: github.com/rs/zerolog +- package: golang.org/x/tools + subpackages: + - cover +- package: golang.org/x/lint + subpackages: + - golint +- package: github.com/axw/gocov + subpackages: + - gocov diff --git a/vendor/go.uber.org/zap/global.go b/vendor/go.uber.org/zap/global.go new file mode 100644 index 0000000..c1ac050 --- /dev/null +++ b/vendor/go.uber.org/zap/global.go @@ -0,0 +1,168 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "bytes" + "fmt" + "log" + "os" + "sync" + + "go.uber.org/zap/zapcore" +) + +const ( + _loggerWriterDepth = 2 + _programmerErrorTemplate = "You've found a bug in zap! Please file a bug at " + + "https://github.com/uber-go/zap/issues/new and reference this error: %v" +) + +var ( + _globalMu sync.RWMutex + _globalL = NewNop() + _globalS = _globalL.Sugar() +) + +// L returns the global Logger, which can be reconfigured with ReplaceGlobals. +// It's safe for concurrent use. +func L() *Logger { + _globalMu.RLock() + l := _globalL + _globalMu.RUnlock() + return l +} + +// S returns the global SugaredLogger, which can be reconfigured with +// ReplaceGlobals. It's safe for concurrent use. +func S() *SugaredLogger { + _globalMu.RLock() + s := _globalS + _globalMu.RUnlock() + return s +} + +// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a +// function to restore the original values. It's safe for concurrent use. +func ReplaceGlobals(logger *Logger) func() { + _globalMu.Lock() + prev := _globalL + _globalL = logger + _globalS = logger.Sugar() + _globalMu.Unlock() + return func() { ReplaceGlobals(prev) } +} + +// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at +// InfoLevel. To redirect the standard library's package-global logging +// functions, use RedirectStdLog instead. +func NewStdLog(l *Logger) *log.Logger { + logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) + f := logger.Info + return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */) +} + +// NewStdLogAt returns *log.Logger which writes to supplied zap logger at +// required level. +func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) { + logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) + logFunc, err := levelToFunc(logger, level) + if err != nil { + return nil, err + } + return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil +} + +// RedirectStdLog redirects output from the standard library's package-global +// logger to the supplied logger at InfoLevel. Since zap already handles caller +// annotations, timestamps, etc., it automatically disables the standard +// library's annotations and prefixing. +// +// It returns a function to restore the original prefix and flags and reset the +// standard library's output to os.Stderr. +func RedirectStdLog(l *Logger) func() { + f, err := redirectStdLogAt(l, InfoLevel) + if err != nil { + // Can't get here, since passing InfoLevel to redirectStdLogAt always + // works. + panic(fmt.Sprintf(_programmerErrorTemplate, err)) + } + return f +} + +// RedirectStdLogAt redirects output from the standard library's package-global +// logger to the supplied logger at the specified level. Since zap already +// handles caller annotations, timestamps, etc., it automatically disables the +// standard library's annotations and prefixing. +// +// It returns a function to restore the original prefix and flags and reset the +// standard library's output to os.Stderr. +func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { + return redirectStdLogAt(l, level) +} + +func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { + flags := log.Flags() + prefix := log.Prefix() + log.SetFlags(0) + log.SetPrefix("") + logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) + logFunc, err := levelToFunc(logger, level) + if err != nil { + return nil, err + } + log.SetOutput(&loggerWriter{logFunc}) + return func() { + log.SetFlags(flags) + log.SetPrefix(prefix) + log.SetOutput(os.Stderr) + }, nil +} + +func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) { + switch lvl { + case DebugLevel: + return logger.Debug, nil + case InfoLevel: + return logger.Info, nil + case WarnLevel: + return logger.Warn, nil + case ErrorLevel: + return logger.Error, nil + case DPanicLevel: + return logger.DPanic, nil + case PanicLevel: + return logger.Panic, nil + case FatalLevel: + return logger.Fatal, nil + } + return nil, fmt.Errorf("unrecognized level: %q", lvl) +} + +type loggerWriter struct { + logFunc func(msg string, fields ...Field) +} + +func (l *loggerWriter) Write(p []byte) (int, error) { + p = bytes.TrimSpace(p) + l.logFunc(string(p)) + return len(p), nil +} diff --git a/vendor/go.uber.org/zap/global_go112.go b/vendor/go.uber.org/zap/global_go112.go new file mode 100644 index 0000000..6b5dbda --- /dev/null +++ b/vendor/go.uber.org/zap/global_go112.go @@ -0,0 +1,26 @@ +// Copyright (c) 2019 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// See #682 for more information. +// +build go1.12 + +package zap + +const _stdLogDefaultDepth = 1 diff --git a/vendor/go.uber.org/zap/global_prego112.go b/vendor/go.uber.org/zap/global_prego112.go new file mode 100644 index 0000000..d3ab9af --- /dev/null +++ b/vendor/go.uber.org/zap/global_prego112.go @@ -0,0 +1,26 @@ +// Copyright (c) 2019 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// See #682 for more information. +// +build !go1.12 + +package zap + +const _stdLogDefaultDepth = 2 diff --git a/vendor/go.uber.org/zap/go.mod b/vendor/go.uber.org/zap/go.mod new file mode 100644 index 0000000..118abda --- /dev/null +++ b/vendor/go.uber.org/zap/go.mod @@ -0,0 +1,12 @@ +module go.uber.org/zap + +go 1.13 + +require ( + github.com/pkg/errors v0.8.1 + github.com/stretchr/testify v1.4.0 + go.uber.org/atomic v1.6.0 + go.uber.org/multierr v1.5.0 + golang.org/x/lint v0.0.0-20190930215403-16217165b5de + honnef.co/go/tools v0.0.1-2019.2.3 +) diff --git a/vendor/go.uber.org/zap/go.sum b/vendor/go.uber.org/zap/go.sum new file mode 100644 index 0000000..99cdb93 --- /dev/null +++ b/vendor/go.uber.org/zap/go.sum @@ -0,0 +1,56 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go new file mode 100644 index 0000000..1b0ecac --- /dev/null +++ b/vendor/go.uber.org/zap/http_handler.go @@ -0,0 +1,81 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "encoding/json" + "fmt" + "net/http" + + "go.uber.org/zap/zapcore" +) + +// ServeHTTP is a simple JSON endpoint that can report on or change the current +// logging level. +// +// GET requests return a JSON description of the current logging level. PUT +// requests change the logging level and expect a payload like: +// {"level":"info"} +// +// It's perfectly safe to change the logging level while a program is running. +func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { + type errorResponse struct { + Error string `json:"error"` + } + type payload struct { + Level *zapcore.Level `json:"level"` + } + + enc := json.NewEncoder(w) + + switch r.Method { + + case http.MethodGet: + current := lvl.Level() + enc.Encode(payload{Level: ¤t}) + + case http.MethodPut: + var req payload + + if errmess := func() string { + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return fmt.Sprintf("Request body must be well-formed JSON: %v", err) + } + if req.Level == nil { + return "Must specify a logging level." + } + return "" + }(); errmess != "" { + w.WriteHeader(http.StatusBadRequest) + enc.Encode(errorResponse{Error: errmess}) + return + } + + lvl.SetLevel(*req.Level) + enc.Encode(req) + + default: + w.WriteHeader(http.StatusMethodNotAllowed) + enc.Encode(errorResponse{ + Error: "Only GET and PUT are supported.", + }) + } +} diff --git a/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go new file mode 100644 index 0000000..dad583a --- /dev/null +++ b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go @@ -0,0 +1,31 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package bufferpool houses zap's shared internal buffer pool. Third-party +// packages can recreate the same functionality with buffers.NewPool. +package bufferpool + +import "go.uber.org/zap/buffer" + +var ( + _pool = buffer.NewPool() + // Get retrieves a buffer from the pool, creating one if necessary. + Get = _pool.Get +) diff --git a/vendor/go.uber.org/zap/internal/color/color.go b/vendor/go.uber.org/zap/internal/color/color.go new file mode 100644 index 0000000..c4d5d02 --- /dev/null +++ b/vendor/go.uber.org/zap/internal/color/color.go @@ -0,0 +1,44 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package color adds coloring functionality for TTY output. +package color + +import "fmt" + +// Foreground colors. +const ( + Black Color = iota + 30 + Red + Green + Yellow + Blue + Magenta + Cyan + White +) + +// Color represents a text color. +type Color uint8 + +// Add adds the coloring to the given string. +func (c Color) Add(s string) string { + return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s) +} diff --git a/vendor/go.uber.org/zap/internal/exit/exit.go b/vendor/go.uber.org/zap/internal/exit/exit.go new file mode 100644 index 0000000..dfc5b05 --- /dev/null +++ b/vendor/go.uber.org/zap/internal/exit/exit.go @@ -0,0 +1,64 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package exit provides stubs so that unit tests can exercise code that calls +// os.Exit(1). +package exit + +import "os" + +var real = func() { os.Exit(1) } + +// Exit normally terminates the process by calling os.Exit(1). If the package +// is stubbed, it instead records a call in the testing spy. +func Exit() { + real() +} + +// A StubbedExit is a testing fake for os.Exit. +type StubbedExit struct { + Exited bool + prev func() +} + +// Stub substitutes a fake for the call to os.Exit(1). +func Stub() *StubbedExit { + s := &StubbedExit{prev: real} + real = s.exit + return s +} + +// WithStub runs the supplied function with Exit stubbed. It returns the stub +// used, so that users can test whether the process would have crashed. +func WithStub(f func()) *StubbedExit { + s := Stub() + defer s.Unstub() + f() + return s +} + +// Unstub restores the previous exit function. +func (se *StubbedExit) Unstub() { + real = se.prev +} + +func (se *StubbedExit) exit() { + se.Exited = true +} diff --git a/vendor/go.uber.org/zap/level.go b/vendor/go.uber.org/zap/level.go new file mode 100644 index 0000000..3567a9a --- /dev/null +++ b/vendor/go.uber.org/zap/level.go @@ -0,0 +1,132 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "go.uber.org/atomic" + "go.uber.org/zap/zapcore" +) + +const ( + // DebugLevel logs are typically voluminous, and are usually disabled in + // production. + DebugLevel = zapcore.DebugLevel + // InfoLevel is the default logging priority. + InfoLevel = zapcore.InfoLevel + // WarnLevel logs are more important than Info, but don't need individual + // human review. + WarnLevel = zapcore.WarnLevel + // ErrorLevel logs are high-priority. If an application is running smoothly, + // it shouldn't generate any error-level logs. + ErrorLevel = zapcore.ErrorLevel + // DPanicLevel logs are particularly important errors. In development the + // logger panics after writing the message. + DPanicLevel = zapcore.DPanicLevel + // PanicLevel logs a message, then panics. + PanicLevel = zapcore.PanicLevel + // FatalLevel logs a message, then calls os.Exit(1). + FatalLevel = zapcore.FatalLevel +) + +// LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with +// an anonymous function. +// +// It's particularly useful when splitting log output between different +// outputs (e.g., standard error and standard out). For sample code, see the +// package-level AdvancedConfiguration example. +type LevelEnablerFunc func(zapcore.Level) bool + +// Enabled calls the wrapped function. +func (f LevelEnablerFunc) Enabled(lvl zapcore.Level) bool { return f(lvl) } + +// An AtomicLevel is an atomically changeable, dynamic logging level. It lets +// you safely change the log level of a tree of loggers (the root logger and +// any children created by adding context) at runtime. +// +// The AtomicLevel itself is an http.Handler that serves a JSON endpoint to +// alter its level. +// +// AtomicLevels must be created with the NewAtomicLevel constructor to allocate +// their internal atomic pointer. +type AtomicLevel struct { + l *atomic.Int32 +} + +// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging +// enabled. +func NewAtomicLevel() AtomicLevel { + return AtomicLevel{ + l: atomic.NewInt32(int32(InfoLevel)), + } +} + +// NewAtomicLevelAt is a convenience function that creates an AtomicLevel +// and then calls SetLevel with the given level. +func NewAtomicLevelAt(l zapcore.Level) AtomicLevel { + a := NewAtomicLevel() + a.SetLevel(l) + return a +} + +// Enabled implements the zapcore.LevelEnabler interface, which allows the +// AtomicLevel to be used in place of traditional static levels. +func (lvl AtomicLevel) Enabled(l zapcore.Level) bool { + return lvl.Level().Enabled(l) +} + +// Level returns the minimum enabled log level. +func (lvl AtomicLevel) Level() zapcore.Level { + return zapcore.Level(int8(lvl.l.Load())) +} + +// SetLevel alters the logging level. +func (lvl AtomicLevel) SetLevel(l zapcore.Level) { + lvl.l.Store(int32(l)) +} + +// String returns the string representation of the underlying Level. +func (lvl AtomicLevel) String() string { + return lvl.Level().String() +} + +// UnmarshalText unmarshals the text to an AtomicLevel. It uses the same text +// representations as the static zapcore.Levels ("debug", "info", "warn", +// "error", "dpanic", "panic", and "fatal"). +func (lvl *AtomicLevel) UnmarshalText(text []byte) error { + if lvl.l == nil { + lvl.l = &atomic.Int32{} + } + + var l zapcore.Level + if err := l.UnmarshalText(text); err != nil { + return err + } + + lvl.SetLevel(l) + return nil +} + +// MarshalText marshals the AtomicLevel to a byte slice. It uses the same +// text representation as the static zapcore.Levels ("debug", "info", "warn", +// "error", "dpanic", "panic", and "fatal"). +func (lvl AtomicLevel) MarshalText() (text []byte, err error) { + return lvl.Level().MarshalText() +} diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go new file mode 100644 index 0000000..cd6e195 --- /dev/null +++ b/vendor/go.uber.org/zap/logger.go @@ -0,0 +1,311 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + "io/ioutil" + "os" + "runtime" + "strings" + "time" + + "go.uber.org/zap/zapcore" +) + +// A Logger provides fast, leveled, structured logging. All methods are safe +// for concurrent use. +// +// The Logger is designed for contexts in which every microsecond and every +// allocation matters, so its API intentionally favors performance and type +// safety over brevity. For most applications, the SugaredLogger strikes a +// better balance between performance and ergonomics. +type Logger struct { + core zapcore.Core + + development bool + name string + errorOutput zapcore.WriteSyncer + + addCaller bool + addStack zapcore.LevelEnabler + + callerSkip int +} + +// New constructs a new Logger from the provided zapcore.Core and Options. If +// the passed zapcore.Core is nil, it falls back to using a no-op +// implementation. +// +// This is the most flexible way to construct a Logger, but also the most +// verbose. For typical use cases, the highly-opinionated presets +// (NewProduction, NewDevelopment, and NewExample) or the Config struct are +// more convenient. +// +// For sample code, see the package-level AdvancedConfiguration example. +func New(core zapcore.Core, options ...Option) *Logger { + if core == nil { + return NewNop() + } + log := &Logger{ + core: core, + errorOutput: zapcore.Lock(os.Stderr), + addStack: zapcore.FatalLevel + 1, + } + return log.WithOptions(options...) +} + +// NewNop returns a no-op Logger. It never writes out logs or internal errors, +// and it never runs user-defined hooks. +// +// Using WithOptions to replace the Core or error output of a no-op Logger can +// re-enable logging. +func NewNop() *Logger { + return &Logger{ + core: zapcore.NewNopCore(), + errorOutput: zapcore.AddSync(ioutil.Discard), + addStack: zapcore.FatalLevel + 1, + } +} + +// NewProduction builds a sensible production Logger that writes InfoLevel and +// above logs to standard error as JSON. +// +// It's a shortcut for NewProductionConfig().Build(...Option). +func NewProduction(options ...Option) (*Logger, error) { + return NewProductionConfig().Build(options...) +} + +// NewDevelopment builds a development Logger that writes DebugLevel and above +// logs to standard error in a human-friendly format. +// +// It's a shortcut for NewDevelopmentConfig().Build(...Option). +func NewDevelopment(options ...Option) (*Logger, error) { + return NewDevelopmentConfig().Build(options...) +} + +// NewExample builds a Logger that's designed for use in zap's testable +// examples. It writes DebugLevel and above logs to standard out as JSON, but +// omits the timestamp and calling function to keep example output +// short and deterministic. +func NewExample(options ...Option) *Logger { + encoderCfg := zapcore.EncoderConfig{ + MessageKey: "msg", + LevelKey: "level", + NameKey: "logger", + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeDuration: zapcore.StringDurationEncoder, + } + core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel) + return New(core).WithOptions(options...) +} + +// Sugar wraps the Logger to provide a more ergonomic, but slightly slower, +// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a +// single application to use both Loggers and SugaredLoggers, converting +// between them on the boundaries of performance-sensitive code. +func (log *Logger) Sugar() *SugaredLogger { + core := log.clone() + core.callerSkip += 2 + return &SugaredLogger{core} +} + +// Named adds a new path segment to the logger's name. Segments are joined by +// periods. By default, Loggers are unnamed. +func (log *Logger) Named(s string) *Logger { + if s == "" { + return log + } + l := log.clone() + if log.name == "" { + l.name = s + } else { + l.name = strings.Join([]string{l.name, s}, ".") + } + return l +} + +// WithOptions clones the current Logger, applies the supplied Options, and +// returns the resulting Logger. It's safe to use concurrently. +func (log *Logger) WithOptions(opts ...Option) *Logger { + c := log.clone() + for _, opt := range opts { + opt.apply(c) + } + return c +} + +// With creates a child logger and adds structured context to it. Fields added +// to the child don't affect the parent, and vice versa. +func (log *Logger) With(fields ...Field) *Logger { + if len(fields) == 0 { + return log + } + l := log.clone() + l.core = l.core.With(fields) + return l +} + +// Check returns a CheckedEntry if logging a message at the specified level +// is enabled. It's a completely optional optimization; in high-performance +// applications, Check can help avoid allocating a slice to hold fields. +func (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { + return log.check(lvl, msg) +} + +// Debug logs a message at DebugLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Debug(msg string, fields ...Field) { + if ce := log.check(DebugLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Info logs a message at InfoLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Info(msg string, fields ...Field) { + if ce := log.check(InfoLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Warn logs a message at WarnLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Warn(msg string, fields ...Field) { + if ce := log.check(WarnLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Error logs a message at ErrorLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Error(msg string, fields ...Field) { + if ce := log.check(ErrorLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// DPanic logs a message at DPanicLevel. The message includes any fields +// passed at the log site, as well as any fields accumulated on the logger. +// +// If the logger is in development mode, it then panics (DPanic means +// "development panic"). This is useful for catching errors that are +// recoverable, but shouldn't ever happen. +func (log *Logger) DPanic(msg string, fields ...Field) { + if ce := log.check(DPanicLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Panic logs a message at PanicLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +// +// The logger then panics, even if logging at PanicLevel is disabled. +func (log *Logger) Panic(msg string, fields ...Field) { + if ce := log.check(PanicLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Fatal logs a message at FatalLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +// +// The logger then calls os.Exit(1), even if logging at FatalLevel is +// disabled. +func (log *Logger) Fatal(msg string, fields ...Field) { + if ce := log.check(FatalLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Sync calls the underlying Core's Sync method, flushing any buffered log +// entries. Applications should take care to call Sync before exiting. +func (log *Logger) Sync() error { + return log.core.Sync() +} + +// Core returns the Logger's underlying zapcore.Core. +func (log *Logger) Core() zapcore.Core { + return log.core +} + +func (log *Logger) clone() *Logger { + copy := *log + return © +} + +func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { + // check must always be called directly by a method in the Logger interface + // (e.g., Check, Info, Fatal). + const callerSkipOffset = 2 + + // Check the level first to reduce the cost of disabled log calls. + // Since Panic and higher may exit, we skip the optimization for those levels. + if lvl < zapcore.DPanicLevel && !log.core.Enabled(lvl) { + return nil + } + + // Create basic checked entry thru the core; this will be non-nil if the + // log message will actually be written somewhere. + ent := zapcore.Entry{ + LoggerName: log.name, + Time: time.Now(), + Level: lvl, + Message: msg, + } + ce := log.core.Check(ent, nil) + willWrite := ce != nil + + // Set up any required terminal behavior. + switch ent.Level { + case zapcore.PanicLevel: + ce = ce.Should(ent, zapcore.WriteThenPanic) + case zapcore.FatalLevel: + ce = ce.Should(ent, zapcore.WriteThenFatal) + case zapcore.DPanicLevel: + if log.development { + ce = ce.Should(ent, zapcore.WriteThenPanic) + } + } + + // Only do further annotation if we're going to write this message; checked + // entries that exist only for terminal behavior don't benefit from + // annotation. + if !willWrite { + return ce + } + + // Thread the error output through to the CheckedEntry. + ce.ErrorOutput = log.errorOutput + if log.addCaller { + ce.Entry.Caller = zapcore.NewEntryCaller(runtime.Caller(log.callerSkip + callerSkipOffset)) + if !ce.Entry.Caller.Defined { + fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC()) + log.errorOutput.Sync() + } + } + if log.addStack.Enabled(ce.Entry.Level) { + ce.Entry.Stack = Stack("").String + } + + return ce +} diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go new file mode 100644 index 0000000..59f1b54 --- /dev/null +++ b/vendor/go.uber.org/zap/options.go @@ -0,0 +1,133 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + + "go.uber.org/zap/zapcore" +) + +// An Option configures a Logger. +type Option interface { + apply(*Logger) +} + +// optionFunc wraps a func so it satisfies the Option interface. +type optionFunc func(*Logger) + +func (f optionFunc) apply(log *Logger) { + f(log) +} + +// WrapCore wraps or replaces the Logger's underlying zapcore.Core. +func WrapCore(f func(zapcore.Core) zapcore.Core) Option { + return optionFunc(func(log *Logger) { + log.core = f(log.core) + }) +} + +// Hooks registers functions which will be called each time the Logger writes +// out an Entry. Repeated use of Hooks is additive. +// +// Hooks are useful for simple side effects, like capturing metrics for the +// number of emitted logs. More complex side effects, including anything that +// requires access to the Entry's structured fields, should be implemented as +// a zapcore.Core instead. See zapcore.RegisterHooks for details. +func Hooks(hooks ...func(zapcore.Entry) error) Option { + return optionFunc(func(log *Logger) { + log.core = zapcore.RegisterHooks(log.core, hooks...) + }) +} + +// Fields adds fields to the Logger. +func Fields(fs ...Field) Option { + return optionFunc(func(log *Logger) { + log.core = log.core.With(fs) + }) +} + +// ErrorOutput sets the destination for errors generated by the Logger. Note +// that this option only affects internal errors; for sample code that sends +// error-level logs to a different location from info- and debug-level logs, +// see the package-level AdvancedConfiguration example. +// +// The supplied WriteSyncer must be safe for concurrent use. The Open and +// zapcore.Lock functions are the simplest ways to protect files with a mutex. +func ErrorOutput(w zapcore.WriteSyncer) Option { + return optionFunc(func(log *Logger) { + log.errorOutput = w + }) +} + +// Development puts the logger in development mode, which makes DPanic-level +// logs panic instead of simply logging an error. +func Development() Option { + return optionFunc(func(log *Logger) { + log.development = true + }) +} + +// AddCaller configures the Logger to annotate each message with the filename +// and line number of zap's caller. See also WithCaller. +func AddCaller() Option { + return WithCaller(true) +} + +// WithCaller configures the Logger to annotate each message with the filename +// and line number of zap's caller, or not, depending on the value of enabled. +// This is a generalized form of AddCaller. +func WithCaller(enabled bool) Option { + return optionFunc(func(log *Logger) { + log.addCaller = enabled + }) +} + +// AddCallerSkip increases the number of callers skipped by caller annotation +// (as enabled by the AddCaller option). When building wrappers around the +// Logger and SugaredLogger, supplying this Option prevents zap from always +// reporting the wrapper code as the caller. +func AddCallerSkip(skip int) Option { + return optionFunc(func(log *Logger) { + log.callerSkip += skip + }) +} + +// AddStacktrace configures the Logger to record a stack trace for all messages at +// or above a given level. +func AddStacktrace(lvl zapcore.LevelEnabler) Option { + return optionFunc(func(log *Logger) { + log.addStack = lvl + }) +} + +// IncreaseLevel increase the level of the logger. It has no effect if +// the passed in level tries to decrease the level of the logger. +func IncreaseLevel(lvl zapcore.LevelEnabler) Option { + return optionFunc(func(log *Logger) { + core, err := zapcore.NewIncreaseLevelCore(log.core, lvl) + if err != nil { + fmt.Fprintf(log.errorOutput, "failed to IncreaseLevel: %v", err) + } else { + log.core = core + } + }) +} diff --git a/vendor/go.uber.org/zap/sink.go b/vendor/go.uber.org/zap/sink.go new file mode 100644 index 0000000..ff0becf --- /dev/null +++ b/vendor/go.uber.org/zap/sink.go @@ -0,0 +1,161 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "errors" + "fmt" + "io" + "net/url" + "os" + "strings" + "sync" + + "go.uber.org/zap/zapcore" +) + +const schemeFile = "file" + +var ( + _sinkMutex sync.RWMutex + _sinkFactories map[string]func(*url.URL) (Sink, error) // keyed by scheme +) + +func init() { + resetSinkRegistry() +} + +func resetSinkRegistry() { + _sinkMutex.Lock() + defer _sinkMutex.Unlock() + + _sinkFactories = map[string]func(*url.URL) (Sink, error){ + schemeFile: newFileSink, + } +} + +// Sink defines the interface to write to and close logger destinations. +type Sink interface { + zapcore.WriteSyncer + io.Closer +} + +type nopCloserSink struct{ zapcore.WriteSyncer } + +func (nopCloserSink) Close() error { return nil } + +type errSinkNotFound struct { + scheme string +} + +func (e *errSinkNotFound) Error() string { + return fmt.Sprintf("no sink found for scheme %q", e.scheme) +} + +// RegisterSink registers a user-supplied factory for all sinks with a +// particular scheme. +// +// All schemes must be ASCII, valid under section 3.1 of RFC 3986 +// (https://tools.ietf.org/html/rfc3986#section-3.1), and must not already +// have a factory registered. Zap automatically registers a factory for the +// "file" scheme. +func RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error { + _sinkMutex.Lock() + defer _sinkMutex.Unlock() + + if scheme == "" { + return errors.New("can't register a sink factory for empty string") + } + normalized, err := normalizeScheme(scheme) + if err != nil { + return fmt.Errorf("%q is not a valid scheme: %v", scheme, err) + } + if _, ok := _sinkFactories[normalized]; ok { + return fmt.Errorf("sink factory already registered for scheme %q", normalized) + } + _sinkFactories[normalized] = factory + return nil +} + +func newSink(rawURL string) (Sink, error) { + u, err := url.Parse(rawURL) + if err != nil { + return nil, fmt.Errorf("can't parse %q as a URL: %v", rawURL, err) + } + if u.Scheme == "" { + u.Scheme = schemeFile + } + + _sinkMutex.RLock() + factory, ok := _sinkFactories[u.Scheme] + _sinkMutex.RUnlock() + if !ok { + return nil, &errSinkNotFound{u.Scheme} + } + return factory(u) +} + +func newFileSink(u *url.URL) (Sink, error) { + if u.User != nil { + return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u) + } + if u.Fragment != "" { + return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u) + } + if u.RawQuery != "" { + return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u) + } + // Error messages are better if we check hostname and port separately. + if u.Port() != "" { + return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u) + } + if hn := u.Hostname(); hn != "" && hn != "localhost" { + return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u) + } + switch u.Path { + case "stdout": + return nopCloserSink{os.Stdout}, nil + case "stderr": + return nopCloserSink{os.Stderr}, nil + } + return os.OpenFile(u.Path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) +} + +func normalizeScheme(s string) (string, error) { + // https://tools.ietf.org/html/rfc3986#section-3.1 + s = strings.ToLower(s) + if first := s[0]; 'a' > first || 'z' < first { + return "", errors.New("must start with a letter") + } + for i := 1; i < len(s); i++ { // iterate over bytes, not runes + c := s[i] + switch { + case 'a' <= c && c <= 'z': + continue + case '0' <= c && c <= '9': + continue + case c == '.' || c == '+' || c == '-': + continue + } + return "", fmt.Errorf("may not contain %q", c) + } + return s, nil +} diff --git a/vendor/go.uber.org/zap/stacktrace.go b/vendor/go.uber.org/zap/stacktrace.go new file mode 100644 index 0000000..100fac2 --- /dev/null +++ b/vendor/go.uber.org/zap/stacktrace.go @@ -0,0 +1,126 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "runtime" + "strings" + "sync" + + "go.uber.org/zap/internal/bufferpool" +) + +const _zapPackage = "go.uber.org/zap" + +var ( + _stacktracePool = sync.Pool{ + New: func() interface{} { + return newProgramCounters(64) + }, + } + + // We add "." and "/" suffixes to the package name to ensure we only match + // the exact package and not any package with the same prefix. + _zapStacktracePrefixes = addPrefix(_zapPackage, ".", "/") + _zapStacktraceVendorContains = addPrefix("/vendor/", _zapStacktracePrefixes...) +) + +func takeStacktrace() string { + buffer := bufferpool.Get() + defer buffer.Free() + programCounters := _stacktracePool.Get().(*programCounters) + defer _stacktracePool.Put(programCounters) + + var numFrames int + for { + // Skip the call to runtime.Counters and takeStacktrace so that the + // program counters start at the caller of takeStacktrace. + numFrames = runtime.Callers(2, programCounters.pcs) + if numFrames < len(programCounters.pcs) { + break + } + // Don't put the too-short counter slice back into the pool; this lets + // the pool adjust if we consistently take deep stacktraces. + programCounters = newProgramCounters(len(programCounters.pcs) * 2) + } + + i := 0 + skipZapFrames := true // skip all consecutive zap frames at the beginning. + frames := runtime.CallersFrames(programCounters.pcs[:numFrames]) + + // Note: On the last iteration, frames.Next() returns false, with a valid + // frame, but we ignore this frame. The last frame is a a runtime frame which + // adds noise, since it's only either runtime.main or runtime.goexit. + for frame, more := frames.Next(); more; frame, more = frames.Next() { + if skipZapFrames && isZapFrame(frame.Function) { + continue + } else { + skipZapFrames = false + } + + if i != 0 { + buffer.AppendByte('\n') + } + i++ + buffer.AppendString(frame.Function) + buffer.AppendByte('\n') + buffer.AppendByte('\t') + buffer.AppendString(frame.File) + buffer.AppendByte(':') + buffer.AppendInt(int64(frame.Line)) + } + + return buffer.String() +} + +func isZapFrame(function string) bool { + for _, prefix := range _zapStacktracePrefixes { + if strings.HasPrefix(function, prefix) { + return true + } + } + + // We can't use a prefix match here since the location of the vendor + // directory affects the prefix. Instead we do a contains match. + for _, contains := range _zapStacktraceVendorContains { + if strings.Contains(function, contains) { + return true + } + } + + return false +} + +type programCounters struct { + pcs []uintptr +} + +func newProgramCounters(size int) *programCounters { + return &programCounters{make([]uintptr, size)} +} + +func addPrefix(prefix string, ss ...string) []string { + withPrefix := make([]string, len(ss)) + for i, s := range ss { + withPrefix[i] = prefix + s + } + return withPrefix +} diff --git a/vendor/go.uber.org/zap/sugar.go b/vendor/go.uber.org/zap/sugar.go new file mode 100644 index 0000000..77ca227 --- /dev/null +++ b/vendor/go.uber.org/zap/sugar.go @@ -0,0 +1,304 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + + "go.uber.org/zap/zapcore" + + "go.uber.org/multierr" +) + +const ( + _oddNumberErrMsg = "Ignored key without a value." + _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys." +) + +// A SugaredLogger wraps the base Logger functionality in a slower, but less +// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar +// method. +// +// Unlike the Logger, the SugaredLogger doesn't insist on structured logging. +// For each log level, it exposes three methods: one for loosely-typed +// structured logging, one for println-style formatting, and one for +// printf-style formatting. For example, SugaredLoggers can produce InfoLevel +// output with Infow ("info with" structured context), Info, or Infof. +type SugaredLogger struct { + base *Logger +} + +// Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring +// is quite inexpensive, so it's reasonable for a single application to use +// both Loggers and SugaredLoggers, converting between them on the boundaries +// of performance-sensitive code. +func (s *SugaredLogger) Desugar() *Logger { + base := s.base.clone() + base.callerSkip -= 2 + return base +} + +// Named adds a sub-scope to the logger's name. See Logger.Named for details. +func (s *SugaredLogger) Named(name string) *SugaredLogger { + return &SugaredLogger{base: s.base.Named(name)} +} + +// With adds a variadic number of fields to the logging context. It accepts a +// mix of strongly-typed Field objects and loosely-typed key-value pairs. When +// processing pairs, the first element of the pair is used as the field key +// and the second as the field value. +// +// For example, +// sugaredLogger.With( +// "hello", "world", +// "failure", errors.New("oh no"), +// Stack(), +// "count", 42, +// "user", User{Name: "alice"}, +// ) +// is the equivalent of +// unsugared.With( +// String("hello", "world"), +// String("failure", "oh no"), +// Stack(), +// Int("count", 42), +// Object("user", User{Name: "alice"}), +// ) +// +// Note that the keys in key-value pairs should be strings. In development, +// passing a non-string key panics. In production, the logger is more +// forgiving: a separate error is logged, but the key-value pair is skipped +// and execution continues. Passing an orphaned key triggers similar behavior: +// panics in development and errors in production. +func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger { + return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)} +} + +// Debug uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Debug(args ...interface{}) { + s.log(DebugLevel, "", args, nil) +} + +// Info uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Info(args ...interface{}) { + s.log(InfoLevel, "", args, nil) +} + +// Warn uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Warn(args ...interface{}) { + s.log(WarnLevel, "", args, nil) +} + +// Error uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Error(args ...interface{}) { + s.log(ErrorLevel, "", args, nil) +} + +// DPanic uses fmt.Sprint to construct and log a message. In development, the +// logger then panics. (See DPanicLevel for details.) +func (s *SugaredLogger) DPanic(args ...interface{}) { + s.log(DPanicLevel, "", args, nil) +} + +// Panic uses fmt.Sprint to construct and log a message, then panics. +func (s *SugaredLogger) Panic(args ...interface{}) { + s.log(PanicLevel, "", args, nil) +} + +// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit. +func (s *SugaredLogger) Fatal(args ...interface{}) { + s.log(FatalLevel, "", args, nil) +} + +// Debugf uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Debugf(template string, args ...interface{}) { + s.log(DebugLevel, template, args, nil) +} + +// Infof uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Infof(template string, args ...interface{}) { + s.log(InfoLevel, template, args, nil) +} + +// Warnf uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Warnf(template string, args ...interface{}) { + s.log(WarnLevel, template, args, nil) +} + +// Errorf uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Errorf(template string, args ...interface{}) { + s.log(ErrorLevel, template, args, nil) +} + +// DPanicf uses fmt.Sprintf to log a templated message. In development, the +// logger then panics. (See DPanicLevel for details.) +func (s *SugaredLogger) DPanicf(template string, args ...interface{}) { + s.log(DPanicLevel, template, args, nil) +} + +// Panicf uses fmt.Sprintf to log a templated message, then panics. +func (s *SugaredLogger) Panicf(template string, args ...interface{}) { + s.log(PanicLevel, template, args, nil) +} + +// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit. +func (s *SugaredLogger) Fatalf(template string, args ...interface{}) { + s.log(FatalLevel, template, args, nil) +} + +// Debugw logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +// +// When debug-level logging is disabled, this is much faster than +// s.With(keysAndValues).Debug(msg) +func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) { + s.log(DebugLevel, msg, nil, keysAndValues) +} + +// Infow logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) { + s.log(InfoLevel, msg, nil, keysAndValues) +} + +// Warnw logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) { + s.log(WarnLevel, msg, nil, keysAndValues) +} + +// Errorw logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) { + s.log(ErrorLevel, msg, nil, keysAndValues) +} + +// DPanicw logs a message with some additional context. In development, the +// logger then panics. (See DPanicLevel for details.) The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) { + s.log(DPanicLevel, msg, nil, keysAndValues) +} + +// Panicw logs a message with some additional context, then panics. The +// variadic key-value pairs are treated as they are in With. +func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) { + s.log(PanicLevel, msg, nil, keysAndValues) +} + +// Fatalw logs a message with some additional context, then calls os.Exit. The +// variadic key-value pairs are treated as they are in With. +func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) { + s.log(FatalLevel, msg, nil, keysAndValues) +} + +// Sync flushes any buffered log entries. +func (s *SugaredLogger) Sync() error { + return s.base.Sync() +} + +func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) { + // If logging at this level is completely disabled, skip the overhead of + // string formatting. + if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) { + return + } + + // Format with Sprint, Sprintf, or neither. + msg := template + if msg == "" && len(fmtArgs) > 0 { + msg = fmt.Sprint(fmtArgs...) + } else if msg != "" && len(fmtArgs) > 0 { + msg = fmt.Sprintf(template, fmtArgs...) + } + + if ce := s.base.Check(lvl, msg); ce != nil { + ce.Write(s.sweetenFields(context)...) + } +} + +func (s *SugaredLogger) sweetenFields(args []interface{}) []Field { + if len(args) == 0 { + return nil + } + + // Allocate enough space for the worst case; if users pass only structured + // fields, we shouldn't penalize them with extra allocations. + fields := make([]Field, 0, len(args)) + var invalid invalidPairs + + for i := 0; i < len(args); { + // This is a strongly-typed field. Consume it and move on. + if f, ok := args[i].(Field); ok { + fields = append(fields, f) + i++ + continue + } + + // Make sure this element isn't a dangling key. + if i == len(args)-1 { + s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i])) + break + } + + // Consume this value and the next, treating them as a key-value pair. If the + // key isn't a string, add this pair to the slice of invalid pairs. + key, val := args[i], args[i+1] + if keyStr, ok := key.(string); !ok { + // Subsequent errors are likely, so allocate once up front. + if cap(invalid) == 0 { + invalid = make(invalidPairs, 0, len(args)/2) + } + invalid = append(invalid, invalidPair{i, key, val}) + } else { + fields = append(fields, Any(keyStr, val)) + } + i += 2 + } + + // If we encountered any invalid key-value pairs, log an error. + if len(invalid) > 0 { + s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid)) + } + return fields +} + +type invalidPair struct { + position int + key, value interface{} +} + +func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddInt64("position", int64(p.position)) + Any("key", p.key).AddTo(enc) + Any("value", p.value).AddTo(enc) + return nil +} + +type invalidPairs []invalidPair + +func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error { + var err error + for i := range ps { + err = multierr.Append(err, enc.AppendObject(ps[i])) + } + return err +} diff --git a/vendor/go.uber.org/zap/time.go b/vendor/go.uber.org/zap/time.go new file mode 100644 index 0000000..c5a1f16 --- /dev/null +++ b/vendor/go.uber.org/zap/time.go @@ -0,0 +1,27 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import "time" + +func timeToMillis(t time.Time) int64 { + return t.UnixNano() / int64(time.Millisecond) +} diff --git a/vendor/go.uber.org/zap/writer.go b/vendor/go.uber.org/zap/writer.go new file mode 100644 index 0000000..86a709a --- /dev/null +++ b/vendor/go.uber.org/zap/writer.go @@ -0,0 +1,99 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + "io" + "io/ioutil" + + "go.uber.org/zap/zapcore" + + "go.uber.org/multierr" +) + +// Open is a high-level wrapper that takes a variadic number of URLs, opens or +// creates each of the specified resources, and combines them into a locked +// WriteSyncer. It also returns any error encountered and a function to close +// any opened files. +// +// Passing no URLs returns a no-op WriteSyncer. Zap handles URLs without a +// scheme and URLs with the "file" scheme. Third-party code may register +// factories for other schemes using RegisterSink. +// +// URLs with the "file" scheme must use absolute paths on the local +// filesystem. No user, password, port, fragments, or query parameters are +// allowed, and the hostname must be empty or "localhost". +// +// Since it's common to write logs to the local filesystem, URLs without a +// scheme (e.g., "/var/log/foo.log") are treated as local file paths. Without +// a scheme, the special paths "stdout" and "stderr" are interpreted as +// os.Stdout and os.Stderr. When specified without a scheme, relative file +// paths also work. +func Open(paths ...string) (zapcore.WriteSyncer, func(), error) { + writers, close, err := open(paths) + if err != nil { + return nil, nil, err + } + + writer := CombineWriteSyncers(writers...) + return writer, close, nil +} + +func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { + writers := make([]zapcore.WriteSyncer, 0, len(paths)) + closers := make([]io.Closer, 0, len(paths)) + close := func() { + for _, c := range closers { + c.Close() + } + } + + var openErr error + for _, path := range paths { + sink, err := newSink(path) + if err != nil { + openErr = multierr.Append(openErr, fmt.Errorf("couldn't open sink %q: %v", path, err)) + continue + } + writers = append(writers, sink) + closers = append(closers, sink) + } + if openErr != nil { + close() + return writers, nil, openErr + } + + return writers, close, nil +} + +// CombineWriteSyncers is a utility that combines multiple WriteSyncers into a +// single, locked WriteSyncer. If no inputs are supplied, it returns a no-op +// WriteSyncer. +// +// It's provided purely as a convenience; the result is no different from +// using zapcore.NewMultiWriteSyncer and zapcore.Lock individually. +func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer { + if len(writers) == 0 { + return zapcore.AddSync(ioutil.Discard) + } + return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...)) +} diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go new file mode 100644 index 0000000..b787596 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go @@ -0,0 +1,147 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "fmt" + "sync" + + "go.uber.org/zap/buffer" + "go.uber.org/zap/internal/bufferpool" +) + +var _sliceEncoderPool = sync.Pool{ + New: func() interface{} { + return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)} + }, +} + +func getSliceEncoder() *sliceArrayEncoder { + return _sliceEncoderPool.Get().(*sliceArrayEncoder) +} + +func putSliceEncoder(e *sliceArrayEncoder) { + e.elems = e.elems[:0] + _sliceEncoderPool.Put(e) +} + +type consoleEncoder struct { + *jsonEncoder +} + +// NewConsoleEncoder creates an encoder whose output is designed for human - +// rather than machine - consumption. It serializes the core log entry data +// (message, level, timestamp, etc.) in a plain-text format and leaves the +// structured context as JSON. +// +// Note that although the console encoder doesn't use the keys specified in the +// encoder configuration, it will omit any element whose key is set to the empty +// string. +func NewConsoleEncoder(cfg EncoderConfig) Encoder { + return consoleEncoder{newJSONEncoder(cfg, true)} +} + +func (c consoleEncoder) Clone() Encoder { + return consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)} +} + +func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { + line := bufferpool.Get() + + // We don't want the entry's metadata to be quoted and escaped (if it's + // encoded as strings), which means that we can't use the JSON encoder. The + // simplest option is to use the memory encoder and fmt.Fprint. + // + // If this ever becomes a performance bottleneck, we can implement + // ArrayEncoder for our plain-text format. + arr := getSliceEncoder() + if c.TimeKey != "" && c.EncodeTime != nil { + c.EncodeTime(ent.Time, arr) + } + if c.LevelKey != "" && c.EncodeLevel != nil { + c.EncodeLevel(ent.Level, arr) + } + if ent.LoggerName != "" && c.NameKey != "" { + nameEncoder := c.EncodeName + + if nameEncoder == nil { + // Fall back to FullNameEncoder for backward compatibility. + nameEncoder = FullNameEncoder + } + + nameEncoder(ent.LoggerName, arr) + } + if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil { + c.EncodeCaller(ent.Caller, arr) + } + for i := range arr.elems { + if i > 0 { + line.AppendByte('\t') + } + fmt.Fprint(line, arr.elems[i]) + } + putSliceEncoder(arr) + + // Add the message itself. + if c.MessageKey != "" { + c.addTabIfNecessary(line) + line.AppendString(ent.Message) + } + + // Add any structured context. + c.writeContext(line, fields) + + // If there's no stacktrace key, honor that; this allows users to force + // single-line output. + if ent.Stack != "" && c.StacktraceKey != "" { + line.AppendByte('\n') + line.AppendString(ent.Stack) + } + + if c.LineEnding != "" { + line.AppendString(c.LineEnding) + } else { + line.AppendString(DefaultLineEnding) + } + return line, nil +} + +func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) { + context := c.jsonEncoder.Clone().(*jsonEncoder) + defer context.buf.Free() + + addFields(context, extra) + context.closeOpenNamespaces() + if context.buf.Len() == 0 { + return + } + + c.addTabIfNecessary(line) + line.AppendByte('{') + line.Write(context.buf.Bytes()) + line.AppendByte('}') +} + +func (c consoleEncoder) addTabIfNecessary(line *buffer.Buffer) { + if line.Len() > 0 { + line.AppendByte('\t') + } +} diff --git a/vendor/go.uber.org/zap/zapcore/core.go b/vendor/go.uber.org/zap/zapcore/core.go new file mode 100644 index 0000000..a1ef8b0 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/core.go @@ -0,0 +1,113 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +// Core is a minimal, fast logger interface. It's designed for library authors +// to wrap in a more user-friendly API. +type Core interface { + LevelEnabler + + // With adds structured context to the Core. + With([]Field) Core + // Check determines whether the supplied Entry should be logged (using the + // embedded LevelEnabler and possibly some extra logic). If the entry + // should be logged, the Core adds itself to the CheckedEntry and returns + // the result. + // + // Callers must use Check before calling Write. + Check(Entry, *CheckedEntry) *CheckedEntry + // Write serializes the Entry and any Fields supplied at the log site and + // writes them to their destination. + // + // If called, Write should always log the Entry and Fields; it should not + // replicate the logic of Check. + Write(Entry, []Field) error + // Sync flushes buffered logs (if any). + Sync() error +} + +type nopCore struct{} + +// NewNopCore returns a no-op Core. +func NewNopCore() Core { return nopCore{} } +func (nopCore) Enabled(Level) bool { return false } +func (n nopCore) With([]Field) Core { return n } +func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce } +func (nopCore) Write(Entry, []Field) error { return nil } +func (nopCore) Sync() error { return nil } + +// NewCore creates a Core that writes logs to a WriteSyncer. +func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core { + return &ioCore{ + LevelEnabler: enab, + enc: enc, + out: ws, + } +} + +type ioCore struct { + LevelEnabler + enc Encoder + out WriteSyncer +} + +func (c *ioCore) With(fields []Field) Core { + clone := c.clone() + addFields(clone.enc, fields) + return clone +} + +func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + if c.Enabled(ent.Level) { + return ce.AddCore(ent, c) + } + return ce +} + +func (c *ioCore) Write(ent Entry, fields []Field) error { + buf, err := c.enc.EncodeEntry(ent, fields) + if err != nil { + return err + } + _, err = c.out.Write(buf.Bytes()) + buf.Free() + if err != nil { + return err + } + if ent.Level > ErrorLevel { + // Since we may be crashing the program, sync the output. Ignore Sync + // errors, pending a clean solution to issue #370. + c.Sync() + } + return nil +} + +func (c *ioCore) Sync() error { + return c.out.Sync() +} + +func (c *ioCore) clone() *ioCore { + return &ioCore{ + LevelEnabler: c.LevelEnabler, + enc: c.enc.Clone(), + out: c.out, + } +} diff --git a/vendor/go.uber.org/zap/zapcore/doc.go b/vendor/go.uber.org/zap/zapcore/doc.go new file mode 100644 index 0000000..31000e9 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/doc.go @@ -0,0 +1,24 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package zapcore defines and implements the low-level interfaces upon which +// zap is built. By providing alternate implementations of these interfaces, +// external packages can extend zap's capabilities. +package zapcore // import "go.uber.org/zap/zapcore" diff --git a/vendor/go.uber.org/zap/zapcore/encoder.go b/vendor/go.uber.org/zap/zapcore/encoder.go new file mode 100644 index 0000000..6c78f7e --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/encoder.go @@ -0,0 +1,401 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "time" + + "go.uber.org/zap/buffer" +) + +// DefaultLineEnding defines the default line ending when writing logs. +// Alternate line endings specified in EncoderConfig can override this +// behavior. +const DefaultLineEnding = "\n" + +// OmitKey defines the key to use when callers want to remove a key from log output. +const OmitKey = "" + +// A LevelEncoder serializes a Level to a primitive type. +type LevelEncoder func(Level, PrimitiveArrayEncoder) + +// LowercaseLevelEncoder serializes a Level to a lowercase string. For example, +// InfoLevel is serialized to "info". +func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + enc.AppendString(l.String()) +} + +// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring. +// For example, InfoLevel is serialized to "info" and colored blue. +func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + s, ok := _levelToLowercaseColorString[l] + if !ok { + s = _unknownLevelColor.Add(l.String()) + } + enc.AppendString(s) +} + +// CapitalLevelEncoder serializes a Level to an all-caps string. For example, +// InfoLevel is serialized to "INFO". +func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + enc.AppendString(l.CapitalString()) +} + +// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color. +// For example, InfoLevel is serialized to "INFO" and colored blue. +func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + s, ok := _levelToCapitalColorString[l] + if !ok { + s = _unknownLevelColor.Add(l.CapitalString()) + } + enc.AppendString(s) +} + +// UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to +// CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder, +// "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else +// is unmarshaled to LowercaseLevelEncoder. +func (e *LevelEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "capital": + *e = CapitalLevelEncoder + case "capitalColor": + *e = CapitalColorLevelEncoder + case "color": + *e = LowercaseColorLevelEncoder + default: + *e = LowercaseLevelEncoder + } + return nil +} + +// A TimeEncoder serializes a time.Time to a primitive type. +type TimeEncoder func(time.Time, PrimitiveArrayEncoder) + +// EpochTimeEncoder serializes a time.Time to a floating-point number of seconds +// since the Unix epoch. +func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + nanos := t.UnixNano() + sec := float64(nanos) / float64(time.Second) + enc.AppendFloat64(sec) +} + +// EpochMillisTimeEncoder serializes a time.Time to a floating-point number of +// milliseconds since the Unix epoch. +func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + nanos := t.UnixNano() + millis := float64(nanos) / float64(time.Millisecond) + enc.AppendFloat64(millis) +} + +// EpochNanosTimeEncoder serializes a time.Time to an integer number of +// nanoseconds since the Unix epoch. +func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + enc.AppendInt64(t.UnixNano()) +} + +func encodeTimeLayout(t time.Time, layout string, enc PrimitiveArrayEncoder) { + type appendTimeEncoder interface { + AppendTimeLayout(time.Time, string) + } + + if enc, ok := enc.(appendTimeEncoder); ok { + enc.AppendTimeLayout(t, layout) + return + } + + enc.AppendString(t.Format(layout)) +} + +// ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string +// with millisecond precision. +// +// If enc supports AppendTimeLayout(t time.Time,layout string), it's used +// instead of appending a pre-formatted string value. +func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc) +} + +// RFC3339TimeEncoder serializes a time.Time to an RFC3339-formatted string. +// +// If enc supports AppendTimeLayout(t time.Time,layout string), it's used +// instead of appending a pre-formatted string value. +func RFC3339TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + encodeTimeLayout(t, time.RFC3339, enc) +} + +// RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339-formatted string +// with nanosecond precision. +// +// If enc supports AppendTimeLayout(t time.Time,layout string), it's used +// instead of appending a pre-formatted string value. +func RFC3339NanoTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + encodeTimeLayout(t, time.RFC3339Nano, enc) +} + +// UnmarshalText unmarshals text to a TimeEncoder. +// "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder. +// "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder. +// "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder. +// "millis" is unmarshaled to EpochMillisTimeEncoder. +// "nanos" is unmarshaled to EpochNanosEncoder. +// Anything else is unmarshaled to EpochTimeEncoder. +func (e *TimeEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "rfc3339nano", "RFC3339Nano": + *e = RFC3339NanoTimeEncoder + case "rfc3339", "RFC3339": + *e = RFC3339TimeEncoder + case "iso8601", "ISO8601": + *e = ISO8601TimeEncoder + case "millis": + *e = EpochMillisTimeEncoder + case "nanos": + *e = EpochNanosTimeEncoder + default: + *e = EpochTimeEncoder + } + return nil +} + +// A DurationEncoder serializes a time.Duration to a primitive type. +type DurationEncoder func(time.Duration, PrimitiveArrayEncoder) + +// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed. +func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { + enc.AppendFloat64(float64(d) / float64(time.Second)) +} + +// NanosDurationEncoder serializes a time.Duration to an integer number of +// nanoseconds elapsed. +func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { + enc.AppendInt64(int64(d)) +} + +// MillisDurationEncoder serializes a time.Duration to an integer number of +// milliseconds elapsed. +func MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { + enc.AppendInt64(d.Nanoseconds() / 1e6) +} + +// StringDurationEncoder serializes a time.Duration using its built-in String +// method. +func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { + enc.AppendString(d.String()) +} + +// UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled +// to StringDurationEncoder, and anything else is unmarshaled to +// NanosDurationEncoder. +func (e *DurationEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "string": + *e = StringDurationEncoder + case "nanos": + *e = NanosDurationEncoder + case "ms": + *e = MillisDurationEncoder + default: + *e = SecondsDurationEncoder + } + return nil +} + +// A CallerEncoder serializes an EntryCaller to a primitive type. +type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder) + +// FullCallerEncoder serializes a caller in /full/path/to/package/file:line +// format. +func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) { + // TODO: consider using a byte-oriented API to save an allocation. + enc.AppendString(caller.String()) +} + +// ShortCallerEncoder serializes a caller in package/file:line format, trimming +// all but the final directory from the full path. +func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) { + // TODO: consider using a byte-oriented API to save an allocation. + enc.AppendString(caller.TrimmedPath()) +} + +// UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to +// FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder. +func (e *CallerEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "full": + *e = FullCallerEncoder + default: + *e = ShortCallerEncoder + } + return nil +} + +// A NameEncoder serializes a period-separated logger name to a primitive +// type. +type NameEncoder func(string, PrimitiveArrayEncoder) + +// FullNameEncoder serializes the logger name as-is. +func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { + enc.AppendString(loggerName) +} + +// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is +// unmarshaled to FullNameEncoder. +func (e *NameEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "full": + *e = FullNameEncoder + default: + *e = FullNameEncoder + } + return nil +} + +// An EncoderConfig allows users to configure the concrete encoders supplied by +// zapcore. +type EncoderConfig struct { + // Set the keys used for each log entry. If any key is empty, that portion + // of the entry is omitted. + MessageKey string `json:"messageKey" yaml:"messageKey"` + LevelKey string `json:"levelKey" yaml:"levelKey"` + TimeKey string `json:"timeKey" yaml:"timeKey"` + NameKey string `json:"nameKey" yaml:"nameKey"` + CallerKey string `json:"callerKey" yaml:"callerKey"` + StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"` + LineEnding string `json:"lineEnding" yaml:"lineEnding"` + // Configure the primitive representations of common complex types. For + // example, some users may want all time.Times serialized as floating-point + // seconds since epoch, while others may prefer ISO8601 strings. + EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` + EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` + EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` + EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` + // Unlike the other primitive type encoders, EncodeName is optional. The + // zero value falls back to FullNameEncoder. + EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"` +} + +// ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a +// map- or struct-like object to the logging context. Like maps, ObjectEncoders +// aren't safe for concurrent use (though typical use shouldn't require locks). +type ObjectEncoder interface { + // Logging-specific marshalers. + AddArray(key string, marshaler ArrayMarshaler) error + AddObject(key string, marshaler ObjectMarshaler) error + + // Built-in types. + AddBinary(key string, value []byte) // for arbitrary bytes + AddByteString(key string, value []byte) // for UTF-8 encoded bytes + AddBool(key string, value bool) + AddComplex128(key string, value complex128) + AddComplex64(key string, value complex64) + AddDuration(key string, value time.Duration) + AddFloat64(key string, value float64) + AddFloat32(key string, value float32) + AddInt(key string, value int) + AddInt64(key string, value int64) + AddInt32(key string, value int32) + AddInt16(key string, value int16) + AddInt8(key string, value int8) + AddString(key, value string) + AddTime(key string, value time.Time) + AddUint(key string, value uint) + AddUint64(key string, value uint64) + AddUint32(key string, value uint32) + AddUint16(key string, value uint16) + AddUint8(key string, value uint8) + AddUintptr(key string, value uintptr) + + // AddReflected uses reflection to serialize arbitrary objects, so it can be + // slow and allocation-heavy. + AddReflected(key string, value interface{}) error + // OpenNamespace opens an isolated namespace where all subsequent fields will + // be added. Applications can use namespaces to prevent key collisions when + // injecting loggers into sub-components or third-party libraries. + OpenNamespace(key string) +} + +// ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding +// array-like objects to the logging context. Of note, it supports mixed-type +// arrays even though they aren't typical in Go. Like slices, ArrayEncoders +// aren't safe for concurrent use (though typical use shouldn't require locks). +type ArrayEncoder interface { + // Built-in types. + PrimitiveArrayEncoder + + // Time-related types. + AppendDuration(time.Duration) + AppendTime(time.Time) + + // Logging-specific marshalers. + AppendArray(ArrayMarshaler) error + AppendObject(ObjectMarshaler) error + + // AppendReflected uses reflection to serialize arbitrary objects, so it's + // slow and allocation-heavy. + AppendReflected(value interface{}) error +} + +// PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals +// only in Go's built-in types. It's included only so that Duration- and +// TimeEncoders cannot trigger infinite recursion. +type PrimitiveArrayEncoder interface { + // Built-in types. + AppendBool(bool) + AppendByteString([]byte) // for UTF-8 encoded bytes + AppendComplex128(complex128) + AppendComplex64(complex64) + AppendFloat64(float64) + AppendFloat32(float32) + AppendInt(int) + AppendInt64(int64) + AppendInt32(int32) + AppendInt16(int16) + AppendInt8(int8) + AppendString(string) + AppendUint(uint) + AppendUint64(uint64) + AppendUint32(uint32) + AppendUint16(uint16) + AppendUint8(uint8) + AppendUintptr(uintptr) +} + +// Encoder is a format-agnostic interface for all log entry marshalers. Since +// log encoders don't need to support the same wide range of use cases as +// general-purpose marshalers, it's possible to make them faster and +// lower-allocation. +// +// Implementations of the ObjectEncoder interface's methods can, of course, +// freely modify the receiver. However, the Clone and EncodeEntry methods will +// be called concurrently and shouldn't modify the receiver. +type Encoder interface { + ObjectEncoder + + // Clone copies the encoder, ensuring that adding fields to the copy doesn't + // affect the original. + Clone() Encoder + + // EncodeEntry encodes an entry and fields, along with any accumulated + // context, into a byte buffer and returns it. Any fields that are empty, + // including fields on the `Entry` type, should be omitted. + EncodeEntry(Entry, []Field) (*buffer.Buffer, error) +} diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go new file mode 100644 index 0000000..8273abd --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/entry.go @@ -0,0 +1,258 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "fmt" + "strings" + "sync" + "time" + + "go.uber.org/zap/internal/bufferpool" + "go.uber.org/zap/internal/exit" + + "go.uber.org/multierr" +) + +var ( + _cePool = sync.Pool{New: func() interface{} { + // Pre-allocate some space for cores. + return &CheckedEntry{ + cores: make([]Core, 4), + } + }} +) + +func getCheckedEntry() *CheckedEntry { + ce := _cePool.Get().(*CheckedEntry) + ce.reset() + return ce +} + +func putCheckedEntry(ce *CheckedEntry) { + if ce == nil { + return + } + _cePool.Put(ce) +} + +// NewEntryCaller makes an EntryCaller from the return signature of +// runtime.Caller. +func NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller { + if !ok { + return EntryCaller{} + } + return EntryCaller{ + PC: pc, + File: file, + Line: line, + Defined: true, + } +} + +// EntryCaller represents the caller of a logging function. +type EntryCaller struct { + Defined bool + PC uintptr + File string + Line int +} + +// String returns the full path and line number of the caller. +func (ec EntryCaller) String() string { + return ec.FullPath() +} + +// FullPath returns a /full/path/to/package/file:line description of the +// caller. +func (ec EntryCaller) FullPath() string { + if !ec.Defined { + return "undefined" + } + buf := bufferpool.Get() + buf.AppendString(ec.File) + buf.AppendByte(':') + buf.AppendInt(int64(ec.Line)) + caller := buf.String() + buf.Free() + return caller +} + +// TrimmedPath returns a package/file:line description of the caller, +// preserving only the leaf directory name and file name. +func (ec EntryCaller) TrimmedPath() string { + if !ec.Defined { + return "undefined" + } + // nb. To make sure we trim the path correctly on Windows too, we + // counter-intuitively need to use '/' and *not* os.PathSeparator here, + // because the path given originates from Go stdlib, specifically + // runtime.Caller() which (as of Mar/17) returns forward slashes even on + // Windows. + // + // See https://github.com/golang/go/issues/3335 + // and https://github.com/golang/go/issues/18151 + // + // for discussion on the issue on Go side. + // + // Find the last separator. + // + idx := strings.LastIndexByte(ec.File, '/') + if idx == -1 { + return ec.FullPath() + } + // Find the penultimate separator. + idx = strings.LastIndexByte(ec.File[:idx], '/') + if idx == -1 { + return ec.FullPath() + } + buf := bufferpool.Get() + // Keep everything after the penultimate separator. + buf.AppendString(ec.File[idx+1:]) + buf.AppendByte(':') + buf.AppendInt(int64(ec.Line)) + caller := buf.String() + buf.Free() + return caller +} + +// An Entry represents a complete log message. The entry's structured context +// is already serialized, but the log level, time, message, and call site +// information are available for inspection and modification. Any fields left +// empty will be omitted when encoding. +// +// Entries are pooled, so any functions that accept them MUST be careful not to +// retain references to them. +type Entry struct { + Level Level + Time time.Time + LoggerName string + Message string + Caller EntryCaller + Stack string +} + +// CheckWriteAction indicates what action to take after a log entry is +// processed. Actions are ordered in increasing severity. +type CheckWriteAction uint8 + +const ( + // WriteThenNoop indicates that nothing special needs to be done. It's the + // default behavior. + WriteThenNoop CheckWriteAction = iota + // WriteThenPanic causes a panic after Write. + WriteThenPanic + // WriteThenFatal causes a fatal os.Exit after Write. + WriteThenFatal +) + +// CheckedEntry is an Entry together with a collection of Cores that have +// already agreed to log it. +// +// CheckedEntry references should be created by calling AddCore or Should on a +// nil *CheckedEntry. References are returned to a pool after Write, and MUST +// NOT be retained after calling their Write method. +type CheckedEntry struct { + Entry + ErrorOutput WriteSyncer + dirty bool // best-effort detection of pool misuse + should CheckWriteAction + cores []Core +} + +func (ce *CheckedEntry) reset() { + ce.Entry = Entry{} + ce.ErrorOutput = nil + ce.dirty = false + ce.should = WriteThenNoop + for i := range ce.cores { + // don't keep references to cores + ce.cores[i] = nil + } + ce.cores = ce.cores[:0] +} + +// Write writes the entry to the stored Cores, returns any errors, and returns +// the CheckedEntry reference to a pool for immediate re-use. Finally, it +// executes any required CheckWriteAction. +func (ce *CheckedEntry) Write(fields ...Field) { + if ce == nil { + return + } + + if ce.dirty { + if ce.ErrorOutput != nil { + // Make a best effort to detect unsafe re-use of this CheckedEntry. + // If the entry is dirty, log an internal error; because the + // CheckedEntry is being used after it was returned to the pool, + // the message may be an amalgamation from multiple call sites. + fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", time.Now(), ce.Entry) + ce.ErrorOutput.Sync() + } + return + } + ce.dirty = true + + var err error + for i := range ce.cores { + err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields)) + } + if ce.ErrorOutput != nil { + if err != nil { + fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", time.Now(), err) + ce.ErrorOutput.Sync() + } + } + + should, msg := ce.should, ce.Message + putCheckedEntry(ce) + + switch should { + case WriteThenPanic: + panic(msg) + case WriteThenFatal: + exit.Exit() + } +} + +// AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be +// used by Core.Check implementations, and is safe to call on nil CheckedEntry +// references. +func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry { + if ce == nil { + ce = getCheckedEntry() + ce.Entry = ent + } + ce.cores = append(ce.cores, core) + return ce +} + +// Should sets this CheckedEntry's CheckWriteAction, which controls whether a +// Core will panic or fatal after writing this log entry. Like AddCore, it's +// safe to call on nil CheckedEntry references. +func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry { + if ce == nil { + ce = getCheckedEntry() + ce.Entry = ent + } + ce.should = should + return ce +} diff --git a/vendor/go.uber.org/zap/zapcore/error.go b/vendor/go.uber.org/zap/zapcore/error.go new file mode 100644 index 0000000..9ba2272 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/error.go @@ -0,0 +1,115 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "fmt" + "sync" +) + +// Encodes the given error into fields of an object. A field with the given +// name is added for the error message. +// +// If the error implements fmt.Formatter, a field with the name ${key}Verbose +// is also added with the full verbose error message. +// +// Finally, if the error implements errorGroup (from go.uber.org/multierr) or +// causer (from github.com/pkg/errors), a ${key}Causes field is added with an +// array of objects containing the errors this error was comprised of. +// +// { +// "error": err.Error(), +// "errorVerbose": fmt.Sprintf("%+v", err), +// "errorCauses": [ +// ... +// ], +// } +func encodeError(key string, err error, enc ObjectEncoder) error { + basic := err.Error() + enc.AddString(key, basic) + + switch e := err.(type) { + case errorGroup: + return enc.AddArray(key+"Causes", errArray(e.Errors())) + case fmt.Formatter: + verbose := fmt.Sprintf("%+v", e) + if verbose != basic { + // This is a rich error type, like those produced by + // github.com/pkg/errors. + enc.AddString(key+"Verbose", verbose) + } + } + return nil +} + +type errorGroup interface { + // Provides read-only access to the underlying list of errors, preferably + // without causing any allocs. + Errors() []error +} + +// Note that errArry and errArrayElem are very similar to the version +// implemented in the top-level error.go file. We can't re-use this because +// that would require exporting errArray as part of the zapcore API. + +// Encodes a list of errors using the standard error encoding logic. +type errArray []error + +func (errs errArray) MarshalLogArray(arr ArrayEncoder) error { + for i := range errs { + if errs[i] == nil { + continue + } + + el := newErrArrayElem(errs[i]) + arr.AppendObject(el) + el.Free() + } + return nil +} + +var _errArrayElemPool = sync.Pool{New: func() interface{} { + return &errArrayElem{} +}} + +// Encodes any error into a {"error": ...} re-using the same errors logic. +// +// May be passed in place of an array to build a single-element array. +type errArrayElem struct{ err error } + +func newErrArrayElem(err error) *errArrayElem { + e := _errArrayElemPool.Get().(*errArrayElem) + e.err = err + return e +} + +func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error { + return arr.AppendObject(e) +} + +func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error { + return encodeError("error", e.err, enc) +} + +func (e *errArrayElem) Free() { + e.err = nil + _errArrayElemPool.Put(e) +} diff --git a/vendor/go.uber.org/zap/zapcore/field.go b/vendor/go.uber.org/zap/zapcore/field.go new file mode 100644 index 0000000..6e05f83 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/field.go @@ -0,0 +1,217 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "bytes" + "fmt" + "math" + "reflect" + "time" +) + +// A FieldType indicates which member of the Field union struct should be used +// and how it should be serialized. +type FieldType uint8 + +const ( + // UnknownType is the default field type. Attempting to add it to an encoder will panic. + UnknownType FieldType = iota + // ArrayMarshalerType indicates that the field carries an ArrayMarshaler. + ArrayMarshalerType + // ObjectMarshalerType indicates that the field carries an ObjectMarshaler. + ObjectMarshalerType + // BinaryType indicates that the field carries an opaque binary blob. + BinaryType + // BoolType indicates that the field carries a bool. + BoolType + // ByteStringType indicates that the field carries UTF-8 encoded bytes. + ByteStringType + // Complex128Type indicates that the field carries a complex128. + Complex128Type + // Complex64Type indicates that the field carries a complex128. + Complex64Type + // DurationType indicates that the field carries a time.Duration. + DurationType + // Float64Type indicates that the field carries a float64. + Float64Type + // Float32Type indicates that the field carries a float32. + Float32Type + // Int64Type indicates that the field carries an int64. + Int64Type + // Int32Type indicates that the field carries an int32. + Int32Type + // Int16Type indicates that the field carries an int16. + Int16Type + // Int8Type indicates that the field carries an int8. + Int8Type + // StringType indicates that the field carries a string. + StringType + // TimeType indicates that the field carries a time.Time that is + // representable by a UnixNano() stored as an int64. + TimeType + // TimeFullType indicates that the field carries a time.Time stored as-is. + TimeFullType + // Uint64Type indicates that the field carries a uint64. + Uint64Type + // Uint32Type indicates that the field carries a uint32. + Uint32Type + // Uint16Type indicates that the field carries a uint16. + Uint16Type + // Uint8Type indicates that the field carries a uint8. + Uint8Type + // UintptrType indicates that the field carries a uintptr. + UintptrType + // ReflectType indicates that the field carries an interface{}, which should + // be serialized using reflection. + ReflectType + // NamespaceType signals the beginning of an isolated namespace. All + // subsequent fields should be added to the new namespace. + NamespaceType + // StringerType indicates that the field carries a fmt.Stringer. + StringerType + // ErrorType indicates that the field carries an error. + ErrorType + // SkipType indicates that the field is a no-op. + SkipType +) + +// A Field is a marshaling operation used to add a key-value pair to a logger's +// context. Most fields are lazily marshaled, so it's inexpensive to add fields +// to disabled debug-level log statements. +type Field struct { + Key string + Type FieldType + Integer int64 + String string + Interface interface{} +} + +// AddTo exports a field through the ObjectEncoder interface. It's primarily +// useful to library authors, and shouldn't be necessary in most applications. +func (f Field) AddTo(enc ObjectEncoder) { + var err error + + switch f.Type { + case ArrayMarshalerType: + err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler)) + case ObjectMarshalerType: + err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler)) + case BinaryType: + enc.AddBinary(f.Key, f.Interface.([]byte)) + case BoolType: + enc.AddBool(f.Key, f.Integer == 1) + case ByteStringType: + enc.AddByteString(f.Key, f.Interface.([]byte)) + case Complex128Type: + enc.AddComplex128(f.Key, f.Interface.(complex128)) + case Complex64Type: + enc.AddComplex64(f.Key, f.Interface.(complex64)) + case DurationType: + enc.AddDuration(f.Key, time.Duration(f.Integer)) + case Float64Type: + enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer))) + case Float32Type: + enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer))) + case Int64Type: + enc.AddInt64(f.Key, f.Integer) + case Int32Type: + enc.AddInt32(f.Key, int32(f.Integer)) + case Int16Type: + enc.AddInt16(f.Key, int16(f.Integer)) + case Int8Type: + enc.AddInt8(f.Key, int8(f.Integer)) + case StringType: + enc.AddString(f.Key, f.String) + case TimeType: + if f.Interface != nil { + enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location))) + } else { + // Fall back to UTC if location is nil. + enc.AddTime(f.Key, time.Unix(0, f.Integer)) + } + case TimeFullType: + enc.AddTime(f.Key, f.Interface.(time.Time)) + case Uint64Type: + enc.AddUint64(f.Key, uint64(f.Integer)) + case Uint32Type: + enc.AddUint32(f.Key, uint32(f.Integer)) + case Uint16Type: + enc.AddUint16(f.Key, uint16(f.Integer)) + case Uint8Type: + enc.AddUint8(f.Key, uint8(f.Integer)) + case UintptrType: + enc.AddUintptr(f.Key, uintptr(f.Integer)) + case ReflectType: + err = enc.AddReflected(f.Key, f.Interface) + case NamespaceType: + enc.OpenNamespace(f.Key) + case StringerType: + err = encodeStringer(f.Key, f.Interface, enc) + case ErrorType: + encodeError(f.Key, f.Interface.(error), enc) + case SkipType: + break + default: + panic(fmt.Sprintf("unknown field type: %v", f)) + } + + if err != nil { + enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error()) + } +} + +// Equals returns whether two fields are equal. For non-primitive types such as +// errors, marshalers, or reflect types, it uses reflect.DeepEqual. +func (f Field) Equals(other Field) bool { + if f.Type != other.Type { + return false + } + if f.Key != other.Key { + return false + } + + switch f.Type { + case BinaryType, ByteStringType: + return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte)) + case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType: + return reflect.DeepEqual(f.Interface, other.Interface) + default: + return f == other + } +} + +func addFields(enc ObjectEncoder, fields []Field) { + for i := range fields { + fields[i].AddTo(enc) + } +} + +func encodeStringer(key string, stringer interface{}, enc ObjectEncoder) (err error) { + defer func() { + if v := recover(); v != nil { + err = fmt.Errorf("PANIC=%v", v) + } + }() + + enc.AddString(key, stringer.(fmt.Stringer).String()) + return +} diff --git a/vendor/go.uber.org/zap/zapcore/hook.go b/vendor/go.uber.org/zap/zapcore/hook.go new file mode 100644 index 0000000..5db4afb --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/hook.go @@ -0,0 +1,68 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "go.uber.org/multierr" + +type hooked struct { + Core + funcs []func(Entry) error +} + +// RegisterHooks wraps a Core and runs a collection of user-defined callback +// hooks each time a message is logged. Execution of the callbacks is blocking. +// +// This offers users an easy way to register simple callbacks (e.g., metrics +// collection) without implementing the full Core interface. +func RegisterHooks(core Core, hooks ...func(Entry) error) Core { + funcs := append([]func(Entry) error{}, hooks...) + return &hooked{ + Core: core, + funcs: funcs, + } +} + +func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + // Let the wrapped Core decide whether to log this message or not. This + // also gives the downstream a chance to register itself directly with the + // CheckedEntry. + if downstream := h.Core.Check(ent, ce); downstream != nil { + return downstream.AddCore(ent, h) + } + return ce +} + +func (h *hooked) With(fields []Field) Core { + return &hooked{ + Core: h.Core.With(fields), + funcs: h.funcs, + } +} + +func (h *hooked) Write(ent Entry, _ []Field) error { + // Since our downstream had a chance to register itself directly with the + // CheckedMessage, we don't need to call it here. + var err error + for i := range h.funcs { + err = multierr.Append(err, h.funcs[i](ent)) + } + return err +} diff --git a/vendor/go.uber.org/zap/zapcore/increase_level.go b/vendor/go.uber.org/zap/zapcore/increase_level.go new file mode 100644 index 0000000..5a17492 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/increase_level.go @@ -0,0 +1,66 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "fmt" + +type levelFilterCore struct { + core Core + level LevelEnabler +} + +// NewIncreaseLevelCore creates a core that can be used to increase the level of +// an existing Core. It cannot be used to decrease the logging level, as it acts +// as a filter before calling the underlying core. If level decreases the log level, +// an error is returned. +func NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error) { + for l := _maxLevel; l >= _minLevel; l-- { + if !core.Enabled(l) && level.Enabled(l) { + return nil, fmt.Errorf("invalid increase level, as level %q is allowed by increased level, but not by existing core", l) + } + } + + return &levelFilterCore{core, level}, nil +} + +func (c *levelFilterCore) Enabled(lvl Level) bool { + return c.level.Enabled(lvl) +} + +func (c *levelFilterCore) With(fields []Field) Core { + return &levelFilterCore{c.core.With(fields), c.level} +} + +func (c *levelFilterCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + if !c.Enabled(ent.Level) { + return ce + } + + return c.core.Check(ent, ce) +} + +func (c *levelFilterCore) Write(ent Entry, fields []Field) error { + return c.core.Write(ent, fields) +} + +func (c *levelFilterCore) Sync() error { + return c.core.Sync() +} diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder.go b/vendor/go.uber.org/zap/zapcore/json_encoder.go new file mode 100644 index 0000000..7facc1b --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/json_encoder.go @@ -0,0 +1,524 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "encoding/base64" + "encoding/json" + "math" + "sync" + "time" + "unicode/utf8" + + "go.uber.org/zap/buffer" + "go.uber.org/zap/internal/bufferpool" +) + +// For JSON-escaping; see jsonEncoder.safeAddString below. +const _hex = "0123456789abcdef" + +var _jsonPool = sync.Pool{New: func() interface{} { + return &jsonEncoder{} +}} + +func getJSONEncoder() *jsonEncoder { + return _jsonPool.Get().(*jsonEncoder) +} + +func putJSONEncoder(enc *jsonEncoder) { + if enc.reflectBuf != nil { + enc.reflectBuf.Free() + } + enc.EncoderConfig = nil + enc.buf = nil + enc.spaced = false + enc.openNamespaces = 0 + enc.reflectBuf = nil + enc.reflectEnc = nil + _jsonPool.Put(enc) +} + +type jsonEncoder struct { + *EncoderConfig + buf *buffer.Buffer + spaced bool // include spaces after colons and commas + openNamespaces int + + // for encoding generic values by reflection + reflectBuf *buffer.Buffer + reflectEnc *json.Encoder +} + +// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder +// appropriately escapes all field keys and values. +// +// Note that the encoder doesn't deduplicate keys, so it's possible to produce +// a message like +// {"foo":"bar","foo":"baz"} +// This is permitted by the JSON specification, but not encouraged. Many +// libraries will ignore duplicate key-value pairs (typically keeping the last +// pair) when unmarshaling, but users should attempt to avoid adding duplicate +// keys. +func NewJSONEncoder(cfg EncoderConfig) Encoder { + return newJSONEncoder(cfg, false) +} + +func newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder { + return &jsonEncoder{ + EncoderConfig: &cfg, + buf: bufferpool.Get(), + spaced: spaced, + } +} + +func (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error { + enc.addKey(key) + return enc.AppendArray(arr) +} + +func (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error { + enc.addKey(key) + return enc.AppendObject(obj) +} + +func (enc *jsonEncoder) AddBinary(key string, val []byte) { + enc.AddString(key, base64.StdEncoding.EncodeToString(val)) +} + +func (enc *jsonEncoder) AddByteString(key string, val []byte) { + enc.addKey(key) + enc.AppendByteString(val) +} + +func (enc *jsonEncoder) AddBool(key string, val bool) { + enc.addKey(key) + enc.AppendBool(val) +} + +func (enc *jsonEncoder) AddComplex128(key string, val complex128) { + enc.addKey(key) + enc.AppendComplex128(val) +} + +func (enc *jsonEncoder) AddDuration(key string, val time.Duration) { + enc.addKey(key) + enc.AppendDuration(val) +} + +func (enc *jsonEncoder) AddFloat64(key string, val float64) { + enc.addKey(key) + enc.AppendFloat64(val) +} + +func (enc *jsonEncoder) AddInt64(key string, val int64) { + enc.addKey(key) + enc.AppendInt64(val) +} + +func (enc *jsonEncoder) resetReflectBuf() { + if enc.reflectBuf == nil { + enc.reflectBuf = bufferpool.Get() + enc.reflectEnc = json.NewEncoder(enc.reflectBuf) + + // For consistency with our custom JSON encoder. + enc.reflectEnc.SetEscapeHTML(false) + } else { + enc.reflectBuf.Reset() + } +} + +var nullLiteralBytes = []byte("null") + +// Only invoke the standard JSON encoder if there is actually something to +// encode; otherwise write JSON null literal directly. +func (enc *jsonEncoder) encodeReflected(obj interface{}) ([]byte, error) { + if obj == nil { + return nullLiteralBytes, nil + } + enc.resetReflectBuf() + if err := enc.reflectEnc.Encode(obj); err != nil { + return nil, err + } + enc.reflectBuf.TrimNewline() + return enc.reflectBuf.Bytes(), nil +} + +func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error { + valueBytes, err := enc.encodeReflected(obj) + if err != nil { + return err + } + enc.addKey(key) + _, err = enc.buf.Write(valueBytes) + return err +} + +func (enc *jsonEncoder) OpenNamespace(key string) { + enc.addKey(key) + enc.buf.AppendByte('{') + enc.openNamespaces++ +} + +func (enc *jsonEncoder) AddString(key, val string) { + enc.addKey(key) + enc.AppendString(val) +} + +func (enc *jsonEncoder) AddTime(key string, val time.Time) { + enc.addKey(key) + enc.AppendTime(val) +} + +func (enc *jsonEncoder) AddUint64(key string, val uint64) { + enc.addKey(key) + enc.AppendUint64(val) +} + +func (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error { + enc.addElementSeparator() + enc.buf.AppendByte('[') + err := arr.MarshalLogArray(enc) + enc.buf.AppendByte(']') + return err +} + +func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error { + enc.addElementSeparator() + enc.buf.AppendByte('{') + err := obj.MarshalLogObject(enc) + enc.buf.AppendByte('}') + return err +} + +func (enc *jsonEncoder) AppendBool(val bool) { + enc.addElementSeparator() + enc.buf.AppendBool(val) +} + +func (enc *jsonEncoder) AppendByteString(val []byte) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddByteString(val) + enc.buf.AppendByte('"') +} + +func (enc *jsonEncoder) AppendComplex128(val complex128) { + enc.addElementSeparator() + // Cast to a platform-independent, fixed-size type. + r, i := float64(real(val)), float64(imag(val)) + enc.buf.AppendByte('"') + // Because we're always in a quoted string, we can use strconv without + // special-casing NaN and +/-Inf. + enc.buf.AppendFloat(r, 64) + enc.buf.AppendByte('+') + enc.buf.AppendFloat(i, 64) + enc.buf.AppendByte('i') + enc.buf.AppendByte('"') +} + +func (enc *jsonEncoder) AppendDuration(val time.Duration) { + cur := enc.buf.Len() + enc.EncodeDuration(val, enc) + if cur == enc.buf.Len() { + // User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep + // JSON valid. + enc.AppendInt64(int64(val)) + } +} + +func (enc *jsonEncoder) AppendInt64(val int64) { + enc.addElementSeparator() + enc.buf.AppendInt(val) +} + +func (enc *jsonEncoder) AppendReflected(val interface{}) error { + valueBytes, err := enc.encodeReflected(val) + if err != nil { + return err + } + enc.addElementSeparator() + _, err = enc.buf.Write(valueBytes) + return err +} + +func (enc *jsonEncoder) AppendString(val string) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddString(val) + enc.buf.AppendByte('"') +} + +func (enc *jsonEncoder) AppendTimeLayout(time time.Time, layout string) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.buf.AppendTime(time, layout) + enc.buf.AppendByte('"') +} + +func (enc *jsonEncoder) AppendTime(val time.Time) { + cur := enc.buf.Len() + enc.EncodeTime(val, enc) + if cur == enc.buf.Len() { + // User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep + // output JSON valid. + enc.AppendInt64(val.UnixNano()) + } +} + +func (enc *jsonEncoder) AppendUint64(val uint64) { + enc.addElementSeparator() + enc.buf.AppendUint(val) +} + +func (enc *jsonEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) } +func (enc *jsonEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) } +func (enc *jsonEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) } +func (enc *jsonEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) } +func (enc *jsonEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) } +func (enc *jsonEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) } + +func (enc *jsonEncoder) Clone() Encoder { + clone := enc.clone() + clone.buf.Write(enc.buf.Bytes()) + return clone +} + +func (enc *jsonEncoder) clone() *jsonEncoder { + clone := getJSONEncoder() + clone.EncoderConfig = enc.EncoderConfig + clone.spaced = enc.spaced + clone.openNamespaces = enc.openNamespaces + clone.buf = bufferpool.Get() + return clone +} + +func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { + final := enc.clone() + final.buf.AppendByte('{') + + if final.LevelKey != "" { + final.addKey(final.LevelKey) + cur := final.buf.Len() + final.EncodeLevel(ent.Level, final) + if cur == final.buf.Len() { + // User-supplied EncodeLevel was a no-op. Fall back to strings to keep + // output JSON valid. + final.AppendString(ent.Level.String()) + } + } + if final.TimeKey != "" { + final.AddTime(final.TimeKey, ent.Time) + } + if ent.LoggerName != "" && final.NameKey != "" { + final.addKey(final.NameKey) + cur := final.buf.Len() + nameEncoder := final.EncodeName + + // if no name encoder provided, fall back to FullNameEncoder for backwards + // compatibility + if nameEncoder == nil { + nameEncoder = FullNameEncoder + } + + nameEncoder(ent.LoggerName, final) + if cur == final.buf.Len() { + // User-supplied EncodeName was a no-op. Fall back to strings to + // keep output JSON valid. + final.AppendString(ent.LoggerName) + } + } + if ent.Caller.Defined && final.CallerKey != "" { + final.addKey(final.CallerKey) + cur := final.buf.Len() + final.EncodeCaller(ent.Caller, final) + if cur == final.buf.Len() { + // User-supplied EncodeCaller was a no-op. Fall back to strings to + // keep output JSON valid. + final.AppendString(ent.Caller.String()) + } + } + if final.MessageKey != "" { + final.addKey(enc.MessageKey) + final.AppendString(ent.Message) + } + if enc.buf.Len() > 0 { + final.addElementSeparator() + final.buf.Write(enc.buf.Bytes()) + } + addFields(final, fields) + final.closeOpenNamespaces() + if ent.Stack != "" && final.StacktraceKey != "" { + final.AddString(final.StacktraceKey, ent.Stack) + } + final.buf.AppendByte('}') + if final.LineEnding != "" { + final.buf.AppendString(final.LineEnding) + } else { + final.buf.AppendString(DefaultLineEnding) + } + + ret := final.buf + putJSONEncoder(final) + return ret, nil +} + +func (enc *jsonEncoder) truncate() { + enc.buf.Reset() +} + +func (enc *jsonEncoder) closeOpenNamespaces() { + for i := 0; i < enc.openNamespaces; i++ { + enc.buf.AppendByte('}') + } +} + +func (enc *jsonEncoder) addKey(key string) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddString(key) + enc.buf.AppendByte('"') + enc.buf.AppendByte(':') + if enc.spaced { + enc.buf.AppendByte(' ') + } +} + +func (enc *jsonEncoder) addElementSeparator() { + last := enc.buf.Len() - 1 + if last < 0 { + return + } + switch enc.buf.Bytes()[last] { + case '{', '[', ':', ',', ' ': + return + default: + enc.buf.AppendByte(',') + if enc.spaced { + enc.buf.AppendByte(' ') + } + } +} + +func (enc *jsonEncoder) appendFloat(val float64, bitSize int) { + enc.addElementSeparator() + switch { + case math.IsNaN(val): + enc.buf.AppendString(`"NaN"`) + case math.IsInf(val, 1): + enc.buf.AppendString(`"+Inf"`) + case math.IsInf(val, -1): + enc.buf.AppendString(`"-Inf"`) + default: + enc.buf.AppendFloat(val, bitSize) + } +} + +// safeAddString JSON-escapes a string and appends it to the internal buffer. +// Unlike the standard library's encoder, it doesn't attempt to protect the +// user from browser vulnerabilities or JSONP-related problems. +func (enc *jsonEncoder) safeAddString(s string) { + for i := 0; i < len(s); { + if enc.tryAddRuneSelf(s[i]) { + i++ + continue + } + r, size := utf8.DecodeRuneInString(s[i:]) + if enc.tryAddRuneError(r, size) { + i++ + continue + } + enc.buf.AppendString(s[i : i+size]) + i += size + } +} + +// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte. +func (enc *jsonEncoder) safeAddByteString(s []byte) { + for i := 0; i < len(s); { + if enc.tryAddRuneSelf(s[i]) { + i++ + continue + } + r, size := utf8.DecodeRune(s[i:]) + if enc.tryAddRuneError(r, size) { + i++ + continue + } + enc.buf.Write(s[i : i+size]) + i += size + } +} + +// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte. +func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool { + if b >= utf8.RuneSelf { + return false + } + if 0x20 <= b && b != '\\' && b != '"' { + enc.buf.AppendByte(b) + return true + } + switch b { + case '\\', '"': + enc.buf.AppendByte('\\') + enc.buf.AppendByte(b) + case '\n': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('n') + case '\r': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('r') + case '\t': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('t') + default: + // Encode bytes < 0x20, except for the escape sequences above. + enc.buf.AppendString(`\u00`) + enc.buf.AppendByte(_hex[b>>4]) + enc.buf.AppendByte(_hex[b&0xF]) + } + return true +} + +func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool { + if r == utf8.RuneError && size == 1 { + enc.buf.AppendString(`\ufffd`) + return true + } + return false +} diff --git a/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go new file mode 100644 index 0000000..e575c9f --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/level.go @@ -0,0 +1,175 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "bytes" + "errors" + "fmt" +) + +var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level") + +// A Level is a logging priority. Higher levels are more important. +type Level int8 + +const ( + // DebugLevel logs are typically voluminous, and are usually disabled in + // production. + DebugLevel Level = iota - 1 + // InfoLevel is the default logging priority. + InfoLevel + // WarnLevel logs are more important than Info, but don't need individual + // human review. + WarnLevel + // ErrorLevel logs are high-priority. If an application is running smoothly, + // it shouldn't generate any error-level logs. + ErrorLevel + // DPanicLevel logs are particularly important errors. In development the + // logger panics after writing the message. + DPanicLevel + // PanicLevel logs a message, then panics. + PanicLevel + // FatalLevel logs a message, then calls os.Exit(1). + FatalLevel + + _minLevel = DebugLevel + _maxLevel = FatalLevel +) + +// String returns a lower-case ASCII representation of the log level. +func (l Level) String() string { + switch l { + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warn" + case ErrorLevel: + return "error" + case DPanicLevel: + return "dpanic" + case PanicLevel: + return "panic" + case FatalLevel: + return "fatal" + default: + return fmt.Sprintf("Level(%d)", l) + } +} + +// CapitalString returns an all-caps ASCII representation of the log level. +func (l Level) CapitalString() string { + // Printing levels in all-caps is common enough that we should export this + // functionality. + switch l { + case DebugLevel: + return "DEBUG" + case InfoLevel: + return "INFO" + case WarnLevel: + return "WARN" + case ErrorLevel: + return "ERROR" + case DPanicLevel: + return "DPANIC" + case PanicLevel: + return "PANIC" + case FatalLevel: + return "FATAL" + default: + return fmt.Sprintf("LEVEL(%d)", l) + } +} + +// MarshalText marshals the Level to text. Note that the text representation +// drops the -Level suffix (see example). +func (l Level) MarshalText() ([]byte, error) { + return []byte(l.String()), nil +} + +// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText +// expects the text representation of a Level to drop the -Level suffix (see +// example). +// +// In particular, this makes it easy to configure logging levels using YAML, +// TOML, or JSON files. +func (l *Level) UnmarshalText(text []byte) error { + if l == nil { + return errUnmarshalNilLevel + } + if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) { + return fmt.Errorf("unrecognized level: %q", text) + } + return nil +} + +func (l *Level) unmarshalText(text []byte) bool { + switch string(text) { + case "debug", "DEBUG": + *l = DebugLevel + case "info", "INFO", "": // make the zero value useful + *l = InfoLevel + case "warn", "WARN": + *l = WarnLevel + case "error", "ERROR": + *l = ErrorLevel + case "dpanic", "DPANIC": + *l = DPanicLevel + case "panic", "PANIC": + *l = PanicLevel + case "fatal", "FATAL": + *l = FatalLevel + default: + return false + } + return true +} + +// Set sets the level for the flag.Value interface. +func (l *Level) Set(s string) error { + return l.UnmarshalText([]byte(s)) +} + +// Get gets the level for the flag.Getter interface. +func (l *Level) Get() interface{} { + return *l +} + +// Enabled returns true if the given level is at or above this level. +func (l Level) Enabled(lvl Level) bool { + return lvl >= l +} + +// LevelEnabler decides whether a given logging level is enabled when logging a +// message. +// +// Enablers are intended to be used to implement deterministic filters; +// concerns like sampling are better implemented as a Core. +// +// Each concrete Level value implements a static LevelEnabler which returns +// true for itself and all higher logging levels. For example WarnLevel.Enabled() +// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and +// FatalLevel, but return false for InfoLevel and DebugLevel. +type LevelEnabler interface { + Enabled(Level) bool +} diff --git a/vendor/go.uber.org/zap/zapcore/level_strings.go b/vendor/go.uber.org/zap/zapcore/level_strings.go new file mode 100644 index 0000000..7af8dad --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/level_strings.go @@ -0,0 +1,46 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "go.uber.org/zap/internal/color" + +var ( + _levelToColor = map[Level]color.Color{ + DebugLevel: color.Magenta, + InfoLevel: color.Blue, + WarnLevel: color.Yellow, + ErrorLevel: color.Red, + DPanicLevel: color.Red, + PanicLevel: color.Red, + FatalLevel: color.Red, + } + _unknownLevelColor = color.Red + + _levelToLowercaseColorString = make(map[Level]string, len(_levelToColor)) + _levelToCapitalColorString = make(map[Level]string, len(_levelToColor)) +) + +func init() { + for level, color := range _levelToColor { + _levelToLowercaseColorString[level] = color.Add(level.String()) + _levelToCapitalColorString[level] = color.Add(level.CapitalString()) + } +} diff --git a/vendor/go.uber.org/zap/zapcore/marshaler.go b/vendor/go.uber.org/zap/zapcore/marshaler.go new file mode 100644 index 0000000..2627a65 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/marshaler.go @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +// ObjectMarshaler allows user-defined types to efficiently add themselves to the +// logging context, and to selectively omit information which shouldn't be +// included in logs (e.g., passwords). +type ObjectMarshaler interface { + MarshalLogObject(ObjectEncoder) error +} + +// ObjectMarshalerFunc is a type adapter that turns a function into an +// ObjectMarshaler. +type ObjectMarshalerFunc func(ObjectEncoder) error + +// MarshalLogObject calls the underlying function. +func (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error { + return f(enc) +} + +// ArrayMarshaler allows user-defined types to efficiently add themselves to the +// logging context, and to selectively omit information which shouldn't be +// included in logs (e.g., passwords). +type ArrayMarshaler interface { + MarshalLogArray(ArrayEncoder) error +} + +// ArrayMarshalerFunc is a type adapter that turns a function into an +// ArrayMarshaler. +type ArrayMarshalerFunc func(ArrayEncoder) error + +// MarshalLogArray calls the underlying function. +func (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error { + return f(enc) +} diff --git a/vendor/go.uber.org/zap/zapcore/memory_encoder.go b/vendor/go.uber.org/zap/zapcore/memory_encoder.go new file mode 100644 index 0000000..dfead08 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/memory_encoder.go @@ -0,0 +1,179 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "time" + +// MapObjectEncoder is an ObjectEncoder backed by a simple +// map[string]interface{}. It's not fast enough for production use, but it's +// helpful in tests. +type MapObjectEncoder struct { + // Fields contains the entire encoded log context. + Fields map[string]interface{} + // cur is a pointer to the namespace we're currently writing to. + cur map[string]interface{} +} + +// NewMapObjectEncoder creates a new map-backed ObjectEncoder. +func NewMapObjectEncoder() *MapObjectEncoder { + m := make(map[string]interface{}) + return &MapObjectEncoder{ + Fields: m, + cur: m, + } +} + +// AddArray implements ObjectEncoder. +func (m *MapObjectEncoder) AddArray(key string, v ArrayMarshaler) error { + arr := &sliceArrayEncoder{elems: make([]interface{}, 0)} + err := v.MarshalLogArray(arr) + m.cur[key] = arr.elems + return err +} + +// AddObject implements ObjectEncoder. +func (m *MapObjectEncoder) AddObject(k string, v ObjectMarshaler) error { + newMap := NewMapObjectEncoder() + m.cur[k] = newMap.Fields + return v.MarshalLogObject(newMap) +} + +// AddBinary implements ObjectEncoder. +func (m *MapObjectEncoder) AddBinary(k string, v []byte) { m.cur[k] = v } + +// AddByteString implements ObjectEncoder. +func (m *MapObjectEncoder) AddByteString(k string, v []byte) { m.cur[k] = string(v) } + +// AddBool implements ObjectEncoder. +func (m *MapObjectEncoder) AddBool(k string, v bool) { m.cur[k] = v } + +// AddDuration implements ObjectEncoder. +func (m MapObjectEncoder) AddDuration(k string, v time.Duration) { m.cur[k] = v } + +// AddComplex128 implements ObjectEncoder. +func (m *MapObjectEncoder) AddComplex128(k string, v complex128) { m.cur[k] = v } + +// AddComplex64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddComplex64(k string, v complex64) { m.cur[k] = v } + +// AddFloat64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddFloat64(k string, v float64) { m.cur[k] = v } + +// AddFloat32 implements ObjectEncoder. +func (m *MapObjectEncoder) AddFloat32(k string, v float32) { m.cur[k] = v } + +// AddInt implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt(k string, v int) { m.cur[k] = v } + +// AddInt64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt64(k string, v int64) { m.cur[k] = v } + +// AddInt32 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt32(k string, v int32) { m.cur[k] = v } + +// AddInt16 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt16(k string, v int16) { m.cur[k] = v } + +// AddInt8 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt8(k string, v int8) { m.cur[k] = v } + +// AddString implements ObjectEncoder. +func (m *MapObjectEncoder) AddString(k string, v string) { m.cur[k] = v } + +// AddTime implements ObjectEncoder. +func (m MapObjectEncoder) AddTime(k string, v time.Time) { m.cur[k] = v } + +// AddUint implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint(k string, v uint) { m.cur[k] = v } + +// AddUint64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint64(k string, v uint64) { m.cur[k] = v } + +// AddUint32 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint32(k string, v uint32) { m.cur[k] = v } + +// AddUint16 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint16(k string, v uint16) { m.cur[k] = v } + +// AddUint8 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint8(k string, v uint8) { m.cur[k] = v } + +// AddUintptr implements ObjectEncoder. +func (m *MapObjectEncoder) AddUintptr(k string, v uintptr) { m.cur[k] = v } + +// AddReflected implements ObjectEncoder. +func (m *MapObjectEncoder) AddReflected(k string, v interface{}) error { + m.cur[k] = v + return nil +} + +// OpenNamespace implements ObjectEncoder. +func (m *MapObjectEncoder) OpenNamespace(k string) { + ns := make(map[string]interface{}) + m.cur[k] = ns + m.cur = ns +} + +// sliceArrayEncoder is an ArrayEncoder backed by a simple []interface{}. Like +// the MapObjectEncoder, it's not designed for production use. +type sliceArrayEncoder struct { + elems []interface{} +} + +func (s *sliceArrayEncoder) AppendArray(v ArrayMarshaler) error { + enc := &sliceArrayEncoder{} + err := v.MarshalLogArray(enc) + s.elems = append(s.elems, enc.elems) + return err +} + +func (s *sliceArrayEncoder) AppendObject(v ObjectMarshaler) error { + m := NewMapObjectEncoder() + err := v.MarshalLogObject(m) + s.elems = append(s.elems, m.Fields) + return err +} + +func (s *sliceArrayEncoder) AppendReflected(v interface{}) error { + s.elems = append(s.elems, v) + return nil +} + +func (s *sliceArrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, string(v)) } +func (s *sliceArrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) } diff --git a/vendor/go.uber.org/zap/zapcore/sampler.go b/vendor/go.uber.org/zap/zapcore/sampler.go new file mode 100644 index 0000000..25f10ca --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/sampler.go @@ -0,0 +1,208 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "time" + + "go.uber.org/atomic" +) + +const ( + _numLevels = _maxLevel - _minLevel + 1 + _countersPerLevel = 4096 +) + +type counter struct { + resetAt atomic.Int64 + counter atomic.Uint64 +} + +type counters [_numLevels][_countersPerLevel]counter + +func newCounters() *counters { + return &counters{} +} + +func (cs *counters) get(lvl Level, key string) *counter { + i := lvl - _minLevel + j := fnv32a(key) % _countersPerLevel + return &cs[i][j] +} + +// fnv32a, adapted from "hash/fnv", but without a []byte(string) alloc +func fnv32a(s string) uint32 { + const ( + offset32 = 2166136261 + prime32 = 16777619 + ) + hash := uint32(offset32) + for i := 0; i < len(s); i++ { + hash ^= uint32(s[i]) + hash *= prime32 + } + return hash +} + +func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 { + tn := t.UnixNano() + resetAfter := c.resetAt.Load() + if resetAfter > tn { + return c.counter.Inc() + } + + c.counter.Store(1) + + newResetAfter := tn + tick.Nanoseconds() + if !c.resetAt.CAS(resetAfter, newResetAfter) { + // We raced with another goroutine trying to reset, and it also reset + // the counter to 1, so we need to reincrement the counter. + return c.counter.Inc() + } + + return 1 +} + +// SamplingDecision is a decision represented as a bit field made by sampler. +// More decisions may be added in the future. +type SamplingDecision uint32 + +const ( + // LogDropped indicates that the Sampler dropped a log entry. + LogDropped SamplingDecision = 1 << iota + // LogSampled indicates that the Sampler sampled a log entry. + LogSampled +) + +// optionFunc wraps a func so it satisfies the SamplerOption interface. +type optionFunc func(*sampler) + +func (f optionFunc) apply(s *sampler) { + f(s) +} + +// SamplerOption configures a Sampler. +type SamplerOption interface { + apply(*sampler) +} + +// nopSamplingHook is the default hook used by sampler. +func nopSamplingHook(Entry, SamplingDecision) {} + +// SamplerHook registers a function which will be called when Sampler makes a +// decision. +// +// This hook may be used to get visibility into the performance of the sampler. +// For example, use it to track metrics of dropped versus sampled logs. +// +// var dropped atomic.Int64 +// zapcore.SamplerHook(func(ent zapcore.Entry, dec zapcore.SamplingDecision) { +// if dec&zapcore.LogDropped > 0 { +// dropped.Inc() +// } +// }) +func SamplerHook(hook func(entry Entry, dec SamplingDecision)) SamplerOption { + return optionFunc(func(s *sampler) { + s.hook = hook + }) +} + +// NewSamplerWithOptions creates a Core that samples incoming entries, which +// caps the CPU and I/O load of logging while attempting to preserve a +// representative subset of your logs. +// +// Zap samples by logging the first N entries with a given level and message +// each tick. If more Entries with the same level and message are seen during +// the same interval, every Mth message is logged and the rest are dropped. +// +// Sampler can be configured to report sampling decisions with the SamplerHook +// option. +// +// Keep in mind that zap's sampling implementation is optimized for speed over +// absolute precision; under load, each tick may be slightly over- or +// under-sampled. +func NewSamplerWithOptions(core Core, tick time.Duration, first, thereafter int, opts ...SamplerOption) Core { + s := &sampler{ + Core: core, + tick: tick, + counts: newCounters(), + first: uint64(first), + thereafter: uint64(thereafter), + hook: nopSamplingHook, + } + for _, opt := range opts { + opt.apply(s) + } + + return s +} + +type sampler struct { + Core + + counts *counters + tick time.Duration + first, thereafter uint64 + hook func(Entry, SamplingDecision) +} + +// NewSampler creates a Core that samples incoming entries, which +// caps the CPU and I/O load of logging while attempting to preserve a +// representative subset of your logs. +// +// Zap samples by logging the first N entries with a given level and message +// each tick. If more Entries with the same level and message are seen during +// the same interval, every Mth message is logged and the rest are dropped. +// +// Keep in mind that zap's sampling implementation is optimized for speed over +// absolute precision; under load, each tick may be slightly over- or +// under-sampled. +// +// Deprecated: use NewSamplerWithOptions. +func NewSampler(core Core, tick time.Duration, first, thereafter int) Core { + return NewSamplerWithOptions(core, tick, first, thereafter) +} + +func (s *sampler) With(fields []Field) Core { + return &sampler{ + Core: s.Core.With(fields), + tick: s.tick, + counts: s.counts, + first: s.first, + thereafter: s.thereafter, + hook: s.hook, + } +} + +func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + if !s.Enabled(ent.Level) { + return ce + } + + counter := s.counts.get(ent.Level, ent.Message) + n := counter.IncCheckReset(ent.Time, s.tick) + if n > s.first && (n-s.first)%s.thereafter != 0 { + s.hook(ent, LogDropped) + return ce + } + s.hook(ent, LogSampled) + return s.Core.Check(ent, ce) +} diff --git a/vendor/go.uber.org/zap/zapcore/tee.go b/vendor/go.uber.org/zap/zapcore/tee.go new file mode 100644 index 0000000..07a32ee --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/tee.go @@ -0,0 +1,81 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "go.uber.org/multierr" + +type multiCore []Core + +// NewTee creates a Core that duplicates log entries into two or more +// underlying Cores. +// +// Calling it with a single Core returns the input unchanged, and calling +// it with no input returns a no-op Core. +func NewTee(cores ...Core) Core { + switch len(cores) { + case 0: + return NewNopCore() + case 1: + return cores[0] + default: + return multiCore(cores) + } +} + +func (mc multiCore) With(fields []Field) Core { + clone := make(multiCore, len(mc)) + for i := range mc { + clone[i] = mc[i].With(fields) + } + return clone +} + +func (mc multiCore) Enabled(lvl Level) bool { + for i := range mc { + if mc[i].Enabled(lvl) { + return true + } + } + return false +} + +func (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + for i := range mc { + ce = mc[i].Check(ent, ce) + } + return ce +} + +func (mc multiCore) Write(ent Entry, fields []Field) error { + var err error + for i := range mc { + err = multierr.Append(err, mc[i].Write(ent, fields)) + } + return err +} + +func (mc multiCore) Sync() error { + var err error + for i := range mc { + err = multierr.Append(err, mc[i].Sync()) + } + return err +} diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer.go b/vendor/go.uber.org/zap/zapcore/write_syncer.go new file mode 100644 index 0000000..209e25f --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/write_syncer.go @@ -0,0 +1,123 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "io" + "sync" + + "go.uber.org/multierr" +) + +// A WriteSyncer is an io.Writer that can also flush any buffered data. Note +// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer. +type WriteSyncer interface { + io.Writer + Sync() error +} + +// AddSync converts an io.Writer to a WriteSyncer. It attempts to be +// intelligent: if the concrete type of the io.Writer implements WriteSyncer, +// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync. +func AddSync(w io.Writer) WriteSyncer { + switch w := w.(type) { + case WriteSyncer: + return w + default: + return writerWrapper{w} + } +} + +type lockedWriteSyncer struct { + sync.Mutex + ws WriteSyncer +} + +// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In +// particular, *os.Files must be locked before use. +func Lock(ws WriteSyncer) WriteSyncer { + if _, ok := ws.(*lockedWriteSyncer); ok { + // no need to layer on another lock + return ws + } + return &lockedWriteSyncer{ws: ws} +} + +func (s *lockedWriteSyncer) Write(bs []byte) (int, error) { + s.Lock() + n, err := s.ws.Write(bs) + s.Unlock() + return n, err +} + +func (s *lockedWriteSyncer) Sync() error { + s.Lock() + err := s.ws.Sync() + s.Unlock() + return err +} + +type writerWrapper struct { + io.Writer +} + +func (w writerWrapper) Sync() error { + return nil +} + +type multiWriteSyncer []WriteSyncer + +// NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes +// and sync calls, much like io.MultiWriter. +func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer { + if len(ws) == 1 { + return ws[0] + } + // Copy to protect against https://github.com/golang/go/issues/7809 + return multiWriteSyncer(append([]WriteSyncer(nil), ws...)) +} + +// See https://golang.org/src/io/multi.go +// When not all underlying syncers write the same number of bytes, +// the smallest number is returned even though Write() is called on +// all of them. +func (ws multiWriteSyncer) Write(p []byte) (int, error) { + var writeErr error + nWritten := 0 + for _, w := range ws { + n, err := w.Write(p) + writeErr = multierr.Append(writeErr, err) + if nWritten == 0 && n != 0 { + nWritten = n + } else if n < nWritten { + nWritten = n + } + } + return nWritten, writeErr +} + +func (ws multiWriteSyncer) Sync() error { + var err error + for _, w := range ws { + err = multierr.Append(err, w.Sync()) + } + return err +} diff --git a/vendor/golang.org/x/net/http2/hpack/huffman.go b/vendor/golang.org/x/net/http2/hpack/huffman.go index b412a96..a1ab2f0 100644 --- a/vendor/golang.org/x/net/http2/hpack/huffman.go +++ b/vendor/golang.org/x/net/http2/hpack/huffman.go @@ -105,7 +105,14 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { return nil } +// incomparable is a zero-width, non-comparable type. Adding it to a struct +// makes that struct also non-comparable, and generally doesn't add +// any size (as long as it's first). +type incomparable [0]func() + type node struct { + _ incomparable + // children is non-nil for internal nodes children *[256]*node diff --git a/vendor/golang.org/x/net/ipv4/header.go b/vendor/golang.org/x/net/ipv4/header.go index 701bd4b..c271ca4 100644 --- a/vendor/golang.org/x/net/ipv4/header.go +++ b/vendor/golang.org/x/net/ipv4/header.go @@ -14,9 +14,8 @@ import ( ) const ( - Version = 4 // protocol version - HeaderLen = 20 // header length without extension headers - maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields + Version = 4 // protocol version + HeaderLen = 20 // header length without extension headers ) type HeaderFlags int