From 6eacf8ef26eaa12fb0dbe5871470e0daa0588a95 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Fri, 16 Aug 2019 22:24:04 +1000 Subject: [PATCH 1/9] fix(https): enable external https verifications The local proxy currently assumes that a verification will take place against either a) a local endpoint or b) an http endpoint. It did not support external hosts on https.o It also did not rewrite the Host header correctly (see https://github.com/golang/go/issues/28168) This change: 1. Rewrites the header during proxying 2. Hard codes the local proxy to run only on http - there is no reason why it should run on https even if the endpoint under test _is_. 3. Opens the door for client configured transports during verification, to enable self-signed certificates to be easily used --- dsl/pact.go | 20 ++++++++++------- proxy/http.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/dsl/pact.go b/dsl/pact.go index 6f978754f..bba282349 100644 --- a/dsl/pact.go +++ b/dsl/pact.go @@ -328,9 +328,11 @@ func (p *Pact) VerifyProviderRaw(request types.VerifyRequest) (types.ProviderVer // Configure HTTP Verification Proxy opts := proxy.Options{ - TargetAddress: fmt.Sprintf("%s:%s", u.Hostname(), u.Port()), - TargetScheme: u.Scheme, - Middleware: m, + TargetAddress: fmt.Sprintf("%s:%s", u.Hostname(), u.Port()), + TargetScheme: u.Scheme, + TargetPath: u.Path, + Middleware: m, + InternalRequestPathPrefix: providerStatesSetupPath, } // Starts the message wrapper API with hooks back to the state handlers @@ -342,13 +344,13 @@ func (p *Pact) VerifyProviderRaw(request types.VerifyRequest) (types.ProviderVer // Backwards compatibility, setup old provider states URL if given // Otherwise point to proxy setupURL := request.ProviderStatesSetupURL - if request.ProviderStatesSetupURL == "" { - setupURL = fmt.Sprintf("%s://localhost:%d/__setup", u.Scheme, port) + if request.ProviderStatesSetupURL == "" && len(request.StateHandlers) > 0 { + setupURL = fmt.Sprintf("http://localhost:%d%s", port, providerStatesSetupPath) } // Construct verifier request verificationRequest := types.VerifyRequest{ - ProviderBaseURL: fmt.Sprintf("%s://localhost:%d", u.Scheme, port), // + ProviderBaseURL: fmt.Sprintf("http://localhost:%d", port), PactURLs: request.PactURLs, BrokerURL: request.BrokerURL, Tags: request.Tags, @@ -413,7 +415,7 @@ var checkCliCompatibility = func() { func BeforeEachMiddleware(BeforeEach types.Hook) proxy.Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/__setup" { + if r.URL.Path == providerStatesSetupPath { log.Println("[DEBUG] executing before hook") err := BeforeEach() @@ -458,7 +460,7 @@ func AfterEachMiddleware(AfterEach types.Hook) proxy.Middleware { func stateHandlerMiddleware(stateHandlers types.StateHandlers) proxy.Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/__setup" { + if r.URL.Path == providerStatesSetupPath { var s *types.ProviderState decoder := json.NewDecoder(r.Body) decoder.Decode(&s) @@ -706,3 +708,5 @@ func (p *Pact) VerifyMessageConsumer(t *testing.T, message *Message, handler Mes return err } + +const providerStatesSetupPath = "/__setup" diff --git a/proxy/http.go b/proxy/http.go index 66db32d0b..fbfc90f1f 100644 --- a/proxy/http.go +++ b/proxy/http.go @@ -6,6 +6,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "strings" "github.com/pact-foundation/pact-go/utils" ) @@ -25,12 +26,18 @@ type Options struct { // TargetAddress is the host:port component to proxy TargetAddress string + // TargetPath is the path on the target to proxy + TargetPath string + // ProxyPort is the port to make available for proxying // Defaults to a random port ProxyPort int // Middleware to apply to the Proxy Middleware []Middleware + + // Internal request prefix for proxy to not rewrite + InternalRequestPathPrefix string } // loggingMiddleware logs requests to the proxy @@ -59,13 +66,16 @@ func chainHandlers(mw ...Middleware) Middleware { // HTTPReverseProxy provides a default setup for proxying // internal components within the framework func HTTPReverseProxy(options Options) (int, error) { + log.Println("[DEBUG] starting new proxy with opts", options) port := options.ProxyPort var err error - proxy := httputil.NewSingleHostReverseProxy(&url.URL{ + url := &url.URL{ Scheme: options.TargetScheme, Host: options.TargetAddress, - }) + Path: options.TargetPath, + } + proxy := createProxy(url, options.InternalRequestPathPrefix) if port == 0 { port, err = utils.GetFreePort() @@ -82,3 +92,48 @@ func HTTPReverseProxy(options Options) (int, error) { return port, nil } + +// Adapted from https://github.com/golang/go/blob/master/src/net/http/httputil/reverseproxy.go +func createProxy(target *url.URL, ignorePrefix string) *httputil.ReverseProxy { + targetQuery := target.RawQuery + director := func(req *http.Request) { + if !strings.HasPrefix(req.URL.Path, ignorePrefix) { + log.Println("[DEBUG] setting proxy to target") + log.Println("[DEBUG] incoming request", req.URL) + req.URL.Scheme = target.Scheme + req.URL.Host = target.Host + req.Host = target.Host + + req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path) + log.Println("[DEBUG] outgoing request", req.URL) + if targetQuery == "" || req.URL.RawQuery == "" { + req.URL.RawQuery = targetQuery + req.URL.RawQuery + } else { + req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery + } + if _, ok := req.Header["User-Agent"]; !ok { + req.Header.Set("User-Agent", "Pact Go") + } + } else { + log.Println("[DEBUG] setting proxy to internal server") + req.URL.Scheme = "http" + req.URL.Host = "localhost" + req.Host = "localhost" + } + } + return &httputil.ReverseProxy{Director: director} +} + +// From httputil package +// https://github.com/golang/go/blob/master/src/net/http/httputil/reverseproxy.go +func singleJoiningSlash(a, b string) string { + aslash := strings.HasSuffix(a, "/") + bslash := strings.HasPrefix(b, "/") + switch { + case aslash && bslash: + return a + b[1:] + case !aslash && !bslash: + return a + "/" + b + } + return a + b +} From 17242e65613661e89a740be5ced8919cd5e403d4 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sat, 17 Aug 2019 21:49:52 +1000 Subject: [PATCH 2/9] test: run example httpbin external provider as part of e2e tests --- examples/httpbin/httpbin_test.go | 46 ++++++++++++++++++++++++++++ examples/pacts/consumer-httpbin.json | 45 +++++++++++++++++++++++++++ scripts/pact.sh | 2 +- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 examples/httpbin/httpbin_test.go create mode 100644 examples/pacts/consumer-httpbin.json diff --git a/examples/httpbin/httpbin_test.go b/examples/httpbin/httpbin_test.go new file mode 100644 index 000000000..a40eab0e8 --- /dev/null +++ b/examples/httpbin/httpbin_test.go @@ -0,0 +1,46 @@ +package provider + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/pact-foundation/pact-go/dsl" + "github.com/pact-foundation/pact-go/types" + "github.com/pact-foundation/pact-go/utils" +) + +// An external HTTPS provider +func TestPact_GinProvider(t *testing.T) { + + pact := createPact() + + // Verify the Provider with local Pact Files + _, err := pact.VerifyProvider(t, types.VerifyRequest{ + ProviderBaseURL: "https://httpbin.org", + PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/consumer-httpbin.json", pactDir))}, + }) + + if err != nil { + t.Fatal(err) + } +} + +// Configuration / Test Data +var dir, _ = os.Getwd() +var pactDir = fmt.Sprintf("%s/../pacts", dir) +var logDir = fmt.Sprintf("%s/log", dir) +var port, _ = utils.GetFreePort() + +// Setup the Pact client. +func createPact() dsl.Pact { + return dsl.Pact{ + Consumer: "jmarie", + Provider: "loginprovider", + LogDir: logDir, + PactDir: pactDir, + DisableToolValidityCheck: true, + LogLevel: "DEBUG", + } +} diff --git a/examples/pacts/consumer-httpbin.json b/examples/pacts/consumer-httpbin.json new file mode 100644 index 000000000..3f145d89f --- /dev/null +++ b/examples/pacts/consumer-httpbin.json @@ -0,0 +1,45 @@ +{ + "consumer": { + "name": "consumer" + }, + "provider": { + "name": "httpbin" + }, + "interactions": [ + { + "description": "A request to the GET echo service", + "request": { + "method": "GET", + "path": "/get" + }, + "response": { + "status": 200 + } + }, + { + "description": "A request to the GET 404 status service", + "request": { + "method": "GET", + "path": "/status/404" + }, + "response": { + "status": 404 + } + }, + { + "description": "A request to the GET 301 status service", + "request": { + "method": "GET", + "path": "/status/301" + }, + "response": { + "status": 301 + } + } + ], + "metadata": { + "pactSpecification": { + "version": "2.0.0" + } + } +} diff --git a/scripts/pact.sh b/scripts/pact.sh index c1f0c571a..0d955ef55 100755 --- a/scripts/pact.sh +++ b/scripts/pact.sh @@ -31,7 +31,7 @@ export PACT_BROKER_PASSWORD="O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1" export PATH="../build/pact/bin:${PATH}" step "Running E2E regression and example projects" -examples=("github.com/pact-foundation/pact-go/examples/consumer/goconsumer" "github.com/pact-foundation/pact-go/examples/go-kit/provider" "github.com/pact-foundation/pact-go/examples/mux/provider" "github.com/pact-foundation/pact-go/examples/gin/provider" "github.com/pact-foundation/pact-go/examples/messages/consumer" "github.com/pact-foundation/pact-go/examples/messages/provider") +examples=("github.com/pact-foundation/pact-go/examples/consumer/goconsumer" "github.com/pact-foundation/pact-go/examples/go-kit/provider" "github.com/pact-foundation/pact-go/examples/mux/provider" "github.com/pact-foundation/pact-go/examples/gin/provider" "github.com/pact-foundation/pact-go/examples/messages/consumer" "github.com/pact-foundation/pact-go/examples/messages/provider" "github.com/pact-foundation/pact-go/examples/httpbin") for example in "${examples[@]}" do From 243a6caaa327cbd3227609cf85e1de80deec084c Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sat, 17 Aug 2019 22:01:32 +1000 Subject: [PATCH 3/9] chore(docs): fix formatting for IPv4Address matcher --- README.md | 76 ++++++++++--------------------------------------------- 1 file changed, 13 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 221601100..05887f361 100644 --- a/README.md +++ b/README.md @@ -34,57 +34,7 @@ Read [Getting started with Pact] for more information for beginners. ## Table of Contents - - -- [Pact Go](#pact-go) - - [Introduction](#introduction) - - [Table of Contents](#table-of-contents) - - [Versions](#versions) - - [Installation](#installation) - - [Go get](#go-get) - - [Installation on \*nix](#installation-on-\nix) - - [Using Pact](#using-pact) - - [HTTP API Testing](#http-api-testing) - - [Consumer Side Testing](#consumer-side-testing) - - [Provider API Testing](#provider-api-testing) - - [Provider Verification](#provider-verification) - - [Provider States](#provider-states) - - [Before and After Hooks](#before-and-after-hooks) - - [Request Filtering](#request-filtering) - - [Example: API with Authorization](#example-api-with-authorization) - - [Lifecycle of a provider verification](#lifecycle-of-a-provider-verification) - - [Publishing pacts to a Pact Broker and Tagging Pacts](#publishing-pacts-to-a-pact-broker-and-tagging-pacts) - - [Publishing from Go code](#publishing-from-go-code) - - [Publishing Provider Verification Results to a Pact Broker](#publishing-provider-verification-results-to-a-pact-broker) - - [Publishing from the CLI](#publishing-from-the-cli) - - [Using the Pact Broker with Basic authentication](#using-the-pact-broker-with-basic-authentication) - - [Using the Pact Broker with Bearer Token authentication](#using-the-pact-broker-with-bearer-token-authentication) - - [Asynchronous API Testing](#asynchronous-api-testing) - - [Consumer](#consumer) - - [Provider (Producer)](#provider-producer) - - [Pact Broker Integration](#pact-broker-integration) - - [Matching](#matching) - - [Matching on types](#matching-on-types) - - [Matching on arrays](#matching-on-arrays) - - [Matching by regular expression](#matching-by-regular-expression) - - [Match common formats](#match-common-formats) - - [Auto-generate matchers from struct tags](#auto-generate-matchers-from-struct-tags) - - [Examples](#examples) - - [HTTP APIs](#http-apis) - - [Asynchronous APIs](#asynchronous-apis) - - [Integrated examples](#integrated-examples) - - [Troubleshooting](#troubleshooting) - - [Splitting tests across multiple files](#splitting-tests-across-multiple-files) - - [Output Logging](#output-logging) - - [Check if the CLI tools are up to date](#check-if-the-cli-tools-are-up-to-date) - - [Disable CLI checks to speed up tests](#disable-cli-checks-to-speed-up-tests) - - [Re-run a specific provider verification test](#re-run-a-specific-provider-verification-test) - - [Contact](#contact) - - [Documentation](#documentation) - - [Roadmap](#roadmap) - - [Contributing](#contributing) - - +autoauto- [Pact Go](#pact-go)auto - [Introduction](#introduction)auto - [Table of Contents](#table-of-contents)auto - [Versions](#versions)auto - [Installation](#installation)auto - [Go get](#go-get)auto - [Installation on \*nix](#installation-on-\nix)auto - [Using Pact](#using-pact)auto - [HTTP API Testing](#http-api-testing)auto - [Consumer Side Testing](#consumer-side-testing)auto - [Provider API Testing](#provider-api-testing)auto - [Provider Verification](#provider-verification)auto - [Provider States](#provider-states)auto - [Before and After Hooks](#before-and-after-hooks)auto - [Request Filtering](#request-filtering)auto - [Example: API with Authorization](#example-api-with-authorization)auto - [Lifecycle of a provider verification](#lifecycle-of-a-provider-verification)auto - [Publishing pacts to a Pact Broker and Tagging Pacts](#publishing-pacts-to-a-pact-broker-and-tagging-pacts)auto - [Publishing from Go code](#publishing-from-go-code)auto - [Publishing Provider Verification Results to a Pact Broker](#publishing-provider-verification-results-to-a-pact-broker)auto - [Publishing from the CLI](#publishing-from-the-cli)auto - [Using the Pact Broker with Basic authentication](#using-the-pact-broker-with-basic-authentication)auto - [Using the Pact Broker with Bearer Token authentication](#using-the-pact-broker-with-bearer-token-authentication)auto - [Asynchronous API Testing](#asynchronous-api-testing)auto - [Consumer](#consumer)auto - [Provider (Producer)](#provider-producer)auto - [Pact Broker Integration](#pact-broker-integration)auto - [Matching](#matching)auto - [Matching on types](#matching-on-types)auto - [Matching on arrays](#matching-on-arrays)auto - [Matching by regular expression](#matching-by-regular-expression)auto - [Match common formats](#match-common-formats)auto - [Auto-generate matchers from struct tags](#auto-generate-matchers-from-struct-tags)auto - [Examples](#examples)auto - [HTTP APIs](#http-apis)auto - [Asynchronous APIs](#asynchronous-apis)auto - [Integrated examples](#integrated-examples)auto - [Troubleshooting](#troubleshooting)auto - [Splitting tests across multiple files](#splitting-tests-across-multiple-files)auto - [Output Logging](#output-logging)auto - [Check if the CLI tools are up to date](#check-if-the-cli-tools-are-up-to-date)auto - [Disable CLI checks to speed up tests](#disable-cli-checks-to-speed-up-tests)auto - [Re-run a specific provider verification test](#re-run-a-specific-provider-verification-test)auto - [Contact](#contact)auto - [Documentation](#documentation)auto - [Roadmap](#roadmap)auto - [Contributing](#contributing)autoauto ## Versions @@ -688,18 +638,18 @@ This example will result in a response body from the mock server that looks like Often times, you find yourself having to re-write regular expressions for common formats. We've created a number of them for you to save you the time: -| method | description | -| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `Identifier()` | Match an ID (e.g. 42) | -| `Integer()` | Match all numbers that are integers (both ints and longs) | -| `Decimal()` | Match all real numbers (floating point and decimal) | -| `HexValue()` | Match all hexadecimal encoded strings | -| `Date()` | Match string containing basic ISO8601 dates (e.g. 2016-01-01) | -| `Timestamp()` | Match a string containing an RFC3339 formatted timestapm (e.g. Mon, 31 Oct 2016 15:21:41 -0400) | -| `Time()` | Match string containing times in ISO date format (e.g. T22:44:30.652Z) | -| `ipIPv4Address | Match string containing IP4 formatted address | -| `IPv6Address()` | Match string containing IP6 formatted address | -| `UUID()` | Match strings containing UUIDs | +| method | description | +| --------------- | ----------------------------------------------------------------------------------------------- | +| `Identifier()` | Match an ID (e.g. 42) | +| `Integer()` | Match all numbers that are integers (both ints and longs) | +| `Decimal()` | Match all real numbers (floating point and decimal) | +| `HexValue()` | Match all hexadecimal encoded strings | +| `Date()` | Match string containing basic ISO8601 dates (e.g. 2016-01-01) | +| `Timestamp()` | Match a string containing an RFC3339 formatted timestapm (e.g. Mon, 31 Oct 2016 15:21:41 -0400) | +| `Time()` | Match string containing times in ISO date format (e.g. T22:44:30.652Z) | +| `IPv4Address()` | Match string containing IP4 formatted address | +| `IPv6Address()` | Match string containing IP6 formatted address | +| `UUID()` | Match strings containing UUIDs | #### Auto-generate matchers from struct tags From fdc6dca4e299dc8de89622b9765bd418ada0cfda Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sat, 17 Aug 2019 22:27:06 +1000 Subject: [PATCH 4/9] feat(tags): adds a better error message when no pacts found to verify. Fixes #105. --- dsl/pact.go | 15 +++++++++++++++ types/verify_request.go | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/dsl/pact.go b/dsl/pact.go index bba282349..e26446023 100644 --- a/dsl/pact.go +++ b/dsl/pact.go @@ -16,6 +16,7 @@ import ( "os" "path/filepath" "reflect" + "strings" "testing" "time" @@ -387,6 +388,20 @@ func (p *Pact) VerifyProviderRaw(request types.VerifyRequest) (types.ProviderVer func (p *Pact) VerifyProvider(t *testing.T, request types.VerifyRequest) (types.ProviderVerifierResponse, error) { res, err := p.VerifyProviderRaw(request) + if len(res.Examples) == 0 { + message := "No pacts found to verifify" + + if len(request.Tags) > 0 { + message = fmt.Sprintf("%s. Check the tags provided (%s) for your broker (%s) are correct", message, strings.Join(request.Tags, ","), request.BrokerURL) + } + + if request.FailIfNoPactsFound { + t.Errorf(message) + } else { + t.Logf(message) + } + } + for _, example := range res.Examples { t.Run(example.Description, func(st *testing.T) { st.Log(example.FullDescription) diff --git a/types/verify_request.go b/types/verify_request.go index e893e89f5..8a7b79956 100644 --- a/types/verify_request.go +++ b/types/verify_request.go @@ -43,6 +43,10 @@ type VerifyRequest struct { // BrokerToken is required when authenticating using the Bearer token mechanism BrokerToken string + // FailIfNoPactsFound configures the framework to return an error + // if no pacts were found when looking up from a broker + FailIfNoPactsFound bool + // PublishVerificationResults to the Pact Broker. PublishVerificationResults bool From c318066d01db0acf4484cccba92c610797da8239 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sat, 17 Aug 2019 22:50:02 +1000 Subject: [PATCH 5/9] test: add e2e regression for CustomProviderHeaers in httpbin test. Fixes #113 --- examples/httpbin/httpbin_test.go | 5 +++-- examples/pacts/consumer-httpbin.json | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/examples/httpbin/httpbin_test.go b/examples/httpbin/httpbin_test.go index a40eab0e8..6d1d46e83 100644 --- a/examples/httpbin/httpbin_test.go +++ b/examples/httpbin/httpbin_test.go @@ -18,8 +18,9 @@ func TestPact_GinProvider(t *testing.T) { // Verify the Provider with local Pact Files _, err := pact.VerifyProvider(t, types.VerifyRequest{ - ProviderBaseURL: "https://httpbin.org", - PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/consumer-httpbin.json", pactDir))}, + ProviderBaseURL: "https://httpbin.org", + PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/consumer-httpbin.json", pactDir))}, + CustomProviderHeaders: []string{"Authorization: Bearer SOME_TOKEN"}, }) if err != nil { diff --git a/examples/pacts/consumer-httpbin.json b/examples/pacts/consumer-httpbin.json index 3f145d89f..d034ce33d 100644 --- a/examples/pacts/consumer-httpbin.json +++ b/examples/pacts/consumer-httpbin.json @@ -27,13 +27,25 @@ } }, { - "description": "A request to the GET 301 status service", + "description": "A GET request to the bearer service", "request": { "method": "GET", - "path": "/status/301" + "path": "/bearer", + "headers": { + "Authorization": "Bearer 1234" + } }, "response": { - "status": 301 + "status": 200, + "body": { + "authenticated": true, + "token": "1234" + }, + "matchingRules": { + "$.body.token": { + "match": "type" + } + } } } ], From fce9b0c9a8ec0dcea728f7a0fafac87375db85ef Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sat, 17 Aug 2019 23:21:51 +1000 Subject: [PATCH 6/9] fix: clear out mock service interactions on verification failure. Fixes #108 --- dsl/pact.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/dsl/pact.go b/dsl/pact.go index e26446023..513a402f4 100644 --- a/dsl/pact.go +++ b/dsl/pact.go @@ -238,6 +238,7 @@ func (p *Pact) Teardown() *Pact { func (p *Pact) Verify(integrationTest func() error) error { p.Setup(true) log.Println("[DEBUG] pact verify") + var err error // Check if we are verifying messages or if we actually have interactions if len(p.Interactions) == 0 { @@ -250,15 +251,23 @@ func (p *Pact) Verify(integrationTest func() error) error { Provider: p.Provider, } + // Cleanup all interactions + defer func(mockServer *MockService) { + log.Println("[DEBUG] clearing interactions") + + p.Interactions = make([]*Interaction, 0) + err = mockServer.DeleteInteractions() + }(mockServer) + for _, interaction := range p.Interactions { - err := mockServer.AddInteraction(interaction) + err = mockServer.AddInteraction(interaction) if err != nil { return err } } // Run the integration test - err := integrationTest() + err = integrationTest() if err != nil { return err } @@ -269,10 +278,7 @@ func (p *Pact) Verify(integrationTest func() error) error { return err } - // Clear out interations - p.Interactions = make([]*Interaction, 0) - - return mockServer.DeleteInteractions() + return err } // WritePact should be called writes when all tests have been performed for a From dc173dbb488778436b5c17ceadd02d40c43c513b Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Thu, 22 Aug 2019 08:56:07 +1000 Subject: [PATCH 7/9] wip: disable TLS verification temporarily --- proxy/http.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/proxy/http.go b/proxy/http.go index fbfc90f1f..84a1260d3 100644 --- a/proxy/http.go +++ b/proxy/http.go @@ -1,12 +1,15 @@ package proxy import ( + "crypto/tls" "fmt" "log" + "net" "net/http" "net/http/httputil" "net/url" "strings" + "time" "github.com/pact-foundation/pact-go/utils" ) @@ -76,6 +79,7 @@ func HTTPReverseProxy(options Options) (int, error) { Path: options.TargetPath, } proxy := createProxy(url, options.InternalRequestPathPrefix) + proxy.Transport = debugTransport{} if port == 0 { port, err = utils.GetFreePort() @@ -93,6 +97,41 @@ func HTTPReverseProxy(options Options) (int, error) { return port, nil } +// https://stackoverflow.com/questions/52986853/how-to-debug-httputil-newsinglehostreverseproxy +// Set the proxy.Transport field to an implementation that dumps the request before delegating to the default transport: + +type debugTransport struct{} + +func (debugTransport) RoundTrip(r *http.Request) (*http.Response, error) { + b, err := httputil.DumpRequestOut(r, false) + if err != nil { + return nil, err + } + log.Println("[TRACE] proxy outgoing request\n", string(b)) + + var DefaultTransport http.RoundTripper = &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + + res, err := DefaultTransport.RoundTrip(r) + b, err = httputil.DumpResponse(res, false) + log.Println("[TRACE] proxied server response\n", string(b)) + + return res, err +} + // Adapted from https://github.com/golang/go/blob/master/src/net/http/httputil/reverseproxy.go func createProxy(target *url.URL, ignorePrefix string) *httputil.ReverseProxy { targetQuery := target.RawQuery From 89b8822087e604e3c75515f9f3a5d9f004fb1af2 Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Thu, 5 Sep 2019 16:08:11 +1000 Subject: [PATCH 8/9] feat(tls): allow custom TLS configuration for provider veriication * Adds new `CustomTLSConfig` to Verifier interface * Fixes issue * Adds integration tests --- Gopkg.lock | 75 +- README.md | 23 +- dsl/pact.go | 3 +- examples/customTls/certs/ca.pem | 19 + examples/customTls/certs/cert-key.pem | 27 + examples/customTls/certs/cert.pem | 19 + examples/customTls/certs/server-cert.pem | 19 + examples/customTls/certs/server-key.pem | 27 + .../customTls/self_signed_certificate_test.go | 86 + examples/httpbin/httpbin_test.go | 15 +- examples/mux/provider/user_service_test.go | 4 +- examples/pacts/consumer-selfsignedtls.json | 25 + proxy/http.go | 26 +- scripts/pact.ps1 | 2 +- scripts/pact.sh | 2 +- types/verify_request.go | 5 + vendor/github.com/go-kit/kit/LICENSE | 22 - vendor/github.com/go-kit/kit/endpoint/doc.go | 5 - .../go-kit/kit/endpoint/endpoint.go | 28 - vendor/github.com/go-kit/kit/log/README.md | 147 -- vendor/github.com/go-kit/kit/log/doc.go | 116 -- .../github.com/go-kit/kit/log/json_logger.go | 89 -- vendor/github.com/go-kit/kit/log/log.go | 135 -- .../go-kit/kit/log/logfmt_logger.go | 62 - .../github.com/go-kit/kit/log/nop_logger.go | 8 - vendor/github.com/go-kit/kit/log/stdlib.go | 116 -- vendor/github.com/go-kit/kit/log/sync.go | 116 -- vendor/github.com/go-kit/kit/log/term/LICENSE | 21 - vendor/github.com/go-kit/kit/log/value.go | 102 -- .../go-kit/kit/transport/http/client.go | 181 --- .../go-kit/kit/transport/http/doc.go | 2 - .../kit/transport/http/encode_decode.go | 30 - .../transport/http/request_response_funcs.go | 133 -- .../go-kit/kit/transport/http/server.go | 233 --- vendor/github.com/go-logfmt/logfmt/.gitignore | 4 - .../github.com/go-logfmt/logfmt/.travis.yml | 15 - vendor/github.com/go-logfmt/logfmt/LICENSE | 22 - vendor/github.com/go-logfmt/logfmt/README.md | 33 - vendor/github.com/go-logfmt/logfmt/decode.go | 237 --- vendor/github.com/go-logfmt/logfmt/doc.go | 6 - vendor/github.com/go-logfmt/logfmt/encode.go | 321 ---- vendor/github.com/go-logfmt/logfmt/fuzz.go | 126 -- .../github.com/go-logfmt/logfmt/jsonstring.go | 277 ---- vendor/github.com/go-stack/stack/.travis.yml | 13 - vendor/github.com/go-stack/stack/LICENSE.md | 21 - vendor/github.com/go-stack/stack/README.md | 38 - vendor/github.com/go-stack/stack/stack.go | 324 ---- vendor/github.com/gorilla/context/.travis.yml | 19 - vendor/github.com/gorilla/context/LICENSE | 27 - vendor/github.com/gorilla/context/README.md | 10 - vendor/github.com/gorilla/context/context.go | 143 -- vendor/github.com/gorilla/context/doc.go | 88 -- vendor/github.com/gorilla/mux/.travis.yml | 23 - .../github.com/gorilla/mux/ISSUE_TEMPLATE.md | 11 - vendor/github.com/gorilla/mux/LICENSE | 27 - vendor/github.com/gorilla/mux/README.md | 649 -------- .../github.com/gorilla/mux/context_gorilla.go | 26 - .../github.com/gorilla/mux/context_native.go | 24 - vendor/github.com/gorilla/mux/doc.go | 306 ---- vendor/github.com/gorilla/mux/middleware.go | 72 - vendor/github.com/gorilla/mux/mux.go | 588 ------- vendor/github.com/gorilla/mux/regexp.go | 332 ---- vendor/github.com/gorilla/mux/route.go | 763 --------- vendor/github.com/gorilla/mux/test_helpers.go | 19 - vendor/github.com/kardianos/osext/README.md | 21 - .../github.com/kardianos/osext/osext_plan9.go | 22 - .../kardianos/osext/osext_procfs.go | 36 - .../kardianos/osext/osext_sysctl.go | 126 -- .../kardianos/osext/osext_windows.go | 36 - vendor/github.com/kr/logfmt/.gitignore | 3 - vendor/github.com/kr/logfmt/Readme | 12 - vendor/github.com/kr/logfmt/decode.go | 184 --- vendor/github.com/kr/logfmt/scanner.go | 149 -- vendor/github.com/kr/logfmt/unquote.go | 149 -- .../spf13/cobra/cobra/cmd/licenses.go | 1384 ----------------- 75 files changed, 313 insertions(+), 8296 deletions(-) create mode 100644 examples/customTls/certs/ca.pem create mode 100644 examples/customTls/certs/cert-key.pem create mode 100644 examples/customTls/certs/cert.pem create mode 100644 examples/customTls/certs/server-cert.pem create mode 100644 examples/customTls/certs/server-key.pem create mode 100644 examples/customTls/self_signed_certificate_test.go create mode 100644 examples/pacts/consumer-selfsignedtls.json delete mode 100644 vendor/github.com/go-kit/kit/LICENSE delete mode 100644 vendor/github.com/go-kit/kit/endpoint/doc.go delete mode 100644 vendor/github.com/go-kit/kit/endpoint/endpoint.go delete mode 100644 vendor/github.com/go-kit/kit/log/README.md delete mode 100644 vendor/github.com/go-kit/kit/log/doc.go delete mode 100644 vendor/github.com/go-kit/kit/log/json_logger.go delete mode 100644 vendor/github.com/go-kit/kit/log/log.go delete mode 100644 vendor/github.com/go-kit/kit/log/logfmt_logger.go delete mode 100644 vendor/github.com/go-kit/kit/log/nop_logger.go delete mode 100644 vendor/github.com/go-kit/kit/log/stdlib.go delete mode 100644 vendor/github.com/go-kit/kit/log/sync.go delete mode 100644 vendor/github.com/go-kit/kit/log/term/LICENSE delete mode 100644 vendor/github.com/go-kit/kit/log/value.go delete mode 100644 vendor/github.com/go-kit/kit/transport/http/client.go delete mode 100644 vendor/github.com/go-kit/kit/transport/http/doc.go delete mode 100644 vendor/github.com/go-kit/kit/transport/http/encode_decode.go delete mode 100644 vendor/github.com/go-kit/kit/transport/http/request_response_funcs.go delete mode 100644 vendor/github.com/go-kit/kit/transport/http/server.go delete mode 100644 vendor/github.com/go-logfmt/logfmt/.gitignore delete mode 100644 vendor/github.com/go-logfmt/logfmt/.travis.yml delete mode 100644 vendor/github.com/go-logfmt/logfmt/LICENSE delete mode 100644 vendor/github.com/go-logfmt/logfmt/README.md delete mode 100644 vendor/github.com/go-logfmt/logfmt/decode.go delete mode 100644 vendor/github.com/go-logfmt/logfmt/doc.go delete mode 100644 vendor/github.com/go-logfmt/logfmt/encode.go delete mode 100644 vendor/github.com/go-logfmt/logfmt/fuzz.go delete mode 100644 vendor/github.com/go-logfmt/logfmt/jsonstring.go delete mode 100644 vendor/github.com/go-stack/stack/.travis.yml delete mode 100644 vendor/github.com/go-stack/stack/LICENSE.md delete mode 100644 vendor/github.com/go-stack/stack/README.md delete mode 100644 vendor/github.com/go-stack/stack/stack.go delete mode 100644 vendor/github.com/gorilla/context/.travis.yml delete mode 100644 vendor/github.com/gorilla/context/LICENSE delete mode 100644 vendor/github.com/gorilla/context/README.md delete mode 100644 vendor/github.com/gorilla/context/context.go delete mode 100644 vendor/github.com/gorilla/context/doc.go delete mode 100644 vendor/github.com/gorilla/mux/.travis.yml delete mode 100644 vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md delete mode 100644 vendor/github.com/gorilla/mux/LICENSE delete mode 100644 vendor/github.com/gorilla/mux/README.md delete mode 100644 vendor/github.com/gorilla/mux/context_gorilla.go delete mode 100644 vendor/github.com/gorilla/mux/context_native.go delete mode 100644 vendor/github.com/gorilla/mux/doc.go delete mode 100644 vendor/github.com/gorilla/mux/middleware.go delete mode 100644 vendor/github.com/gorilla/mux/mux.go delete mode 100644 vendor/github.com/gorilla/mux/regexp.go delete mode 100644 vendor/github.com/gorilla/mux/route.go delete mode 100644 vendor/github.com/gorilla/mux/test_helpers.go delete mode 100644 vendor/github.com/kardianos/osext/README.md delete mode 100644 vendor/github.com/kardianos/osext/osext_plan9.go delete mode 100644 vendor/github.com/kardianos/osext/osext_procfs.go delete mode 100644 vendor/github.com/kardianos/osext/osext_sysctl.go delete mode 100644 vendor/github.com/kardianos/osext/osext_windows.go delete mode 100644 vendor/github.com/kr/logfmt/.gitignore delete mode 100644 vendor/github.com/kr/logfmt/Readme delete mode 100644 vendor/github.com/kr/logfmt/decode.go delete mode 100644 vendor/github.com/kr/logfmt/scanner.go delete mode 100644 vendor/github.com/kr/logfmt/unquote.go delete mode 100644 vendor/github.com/spf13/cobra/cobra/cmd/licenses.go diff --git a/Gopkg.lock b/Gopkg.lock index 1b0c8ed7b..7efb9fdb8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,125 +3,116 @@ [[projects]] branch = "master" + digest = "1:36fe9527deed01d2a317617e59304eb2c4ce9f8a24115bcc5c2e37b3aee5bae4" name = "github.com/gin-contrib/sse" packages = ["."] + pruneopts = "UT" revision = "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae" [[projects]] + digest = "1:489e108f21464371ebf9cb5c30b1eceb07c6dd772dff073919267493dd9d04ea" name = "github.com/gin-gonic/gin" packages = [ ".", "binding", - "render" + "render", ] + pruneopts = "UT" revision = "d459835d2b077e44f7c9b453505ee29881d5d12d" version = "v1.2" [[projects]] - name = "github.com/go-kit/kit" - packages = [ - "endpoint", - "log", - "transport/http" - ] - revision = "ca4112baa34cb55091301bdc13b1420a122b1b9e" - version = "v0.7.0" - -[[projects]] - name = "github.com/go-logfmt/logfmt" - packages = ["."] - revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" - version = "v0.3.0" - -[[projects]] - name = "github.com/go-stack/stack" - packages = ["."] - revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" - version = "v1.7.0" - -[[projects]] + digest = "1:15042ad3498153684d09f393bbaec6b216c8eec6d61f63dff711de7d64ed8861" name = "github.com/golang/protobuf" packages = ["proto"] + pruneopts = "UT" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" -[[projects]] - name = "github.com/gorilla/context" - packages = ["."] - revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" - version = "v1.1.1" - -[[projects]] - name = "github.com/gorilla/mux" - packages = ["."] - revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" - version = "v1.6.2" - [[projects]] branch = "master" + digest = "1:e12b92b8bb20af6e299e9829534cfe790857702a988d3f0443e772c9d82a4fd2" name = "github.com/hashicorp/go-version" packages = ["."] + pruneopts = "UT" revision = "23480c0665776210b5fbbac6eaaee40e3e6a96b7" [[projects]] branch = "master" + digest = "1:ce3f7860fd68bd2dd4c3735e2aed8c9de7c7d05bd6ad7d97a6bedcf4fe7b84fb" name = "github.com/hashicorp/logutils" packages = ["."] + pruneopts = "UT" revision = "0dc08b1671f34c4250ce212759ebd880f743d883" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "UT" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] - branch = "master" - name = "github.com/kr/logfmt" - packages = ["."] - revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" - -[[projects]] + digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb" name = "github.com/mattn/go-isatty" packages = ["."] + pruneopts = "UT" revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" [[projects]] + digest = "1:31f9d9941245e05e806636e3e77fd3bfb21e8668d7d03d47ea71c8ebda21dfec" name = "github.com/spf13/cobra" packages = ["."] + pruneopts = "UT" revision = "f447048345b64b3247b29a679a14bd0da12c7f2f" [[projects]] + digest = "1:00b1571a9044e9977c1e6ddd9a287058d6af85e4385b5fed1e70dea79527fd0c" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "UT" revision = "cb88ea77998c3f024757528e3305022ab50b43be" [[projects]] + digest = "1:03aa6e485e528acb119fb32901cf99582c380225fc7d5a02758e08b180cb56c3" name = "github.com/ugorji/go" packages = ["codec"] + pruneopts = "UT" revision = "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab" version = "v1.1.1" [[projects]] + digest = "1:7bcd7406893de9d36236adc3ab85563c2ec30fcd97194b6a1835e798a65edf09" name = "golang.org/x/sys" packages = ["unix"] + pruneopts = "UT" revision = "076b546753157f758b316e59bcb51e6807c04057" [[projects]] + digest = "1:cbc72c4c4886a918d6ab4b95e347ffe259846260f99ebdd8a198c2331cf2b2e9" name = "gopkg.in/go-playground/validator.v8" packages = ["."] + pruneopts = "UT" revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf" version = "v8.18.2" [[projects]] + digest = "1:858458e1fcdb1cb44ded97c2ae2b610ee9336fdb0a8347ea1ad835e6a1db6c76" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "UT" revision = "a83829b6f1293c91addabc89d0571c246397bbf4" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "12ec2bff4745258b1c4a2e451de650f4fb0f74d32b3f03f17ef53b699c259a8c" + input-imports = [ + "github.com/gin-gonic/gin", + "github.com/hashicorp/go-version", + "github.com/hashicorp/logutils", + "github.com/spf13/cobra", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/README.md b/README.md index 05887f361..79fab4de6 100644 --- a/README.md +++ b/README.md @@ -382,7 +382,7 @@ _Important Note_: You should only use this feature for things that can not be pe For each _interaction_ in a pact file, the order of execution is as follows: -`BeforeEach` -> `StateHandler` -> `RequestFilter (pre)`, `Execute Provider Test` -> `RequestFilter (post)` -> `AfterEach` +`BeforeEach` -> `StateHandler` -> `RequestFilter (pre)` -> `Execute Provider Test` -> `RequestFilter (post)` -> `AfterEach` If any of the middleware or hooks fail, the tests will also fail. @@ -807,6 +807,27 @@ cd examples/message/provider PACT_DESCRIPTION="a user" PACT_PROVIDER_STATE="user with id 127 exists" go test -v . ``` +### Verifying APIs with a self-signed certificate + +Supply your own TLS configuration to customise the behaviour of the runtime: + +```go + _, err := pact.VerifyProvider(t, types.VerifyRequest{ + ProviderBaseURL: "https://localhost:8080", + PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/consumer-selfsignedtls.json", pactDir))}, + CustomTLSConfig: &tls.Config{ + RootCAs: getCaCertPool(), // Specify a custom CA pool + // InsecureSkipVerify: true, // Disable SSL verification altogether + }, + }) +``` + +See [self-signed certificate](https://github.com/pact-foundation/pact-go/examles/customTls/self_signed_certificate_test.go) for an example. + +### Testing AWS API Gateway APIs + +AWS changed their certificate authority last year, and not all OSs have the latest CA chains. If you can't update to the latest certificate bunidles, see "Verifying APIs with a self-signed certificate" for how to work around this. + ## Contact Join us in slack: [![slack](http://slack.pact.io/badge.svg)](http://slack.pact.io) diff --git a/dsl/pact.go b/dsl/pact.go index 513a402f4..e57a36919 100644 --- a/dsl/pact.go +++ b/dsl/pact.go @@ -340,6 +340,7 @@ func (p *Pact) VerifyProviderRaw(request types.VerifyRequest) (types.ProviderVer TargetPath: u.Path, Middleware: m, InternalRequestPathPrefix: providerStatesSetupPath, + CustomTLSConfig: request.CustomTLSConfig, } // Starts the message wrapper API with hooks back to the state handlers @@ -459,7 +460,7 @@ func AfterEachMiddleware(AfterEach types.Hook) proxy.Middleware { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) - if r.URL.Path != "/__setup" { + if r.URL.Path != providerStatesSetupPath { log.Println("[DEBUG] executing after hook") err := AfterEach() diff --git a/examples/customTls/certs/ca.pem b/examples/customTls/certs/ca.pem new file mode 100644 index 000000000..0c5348a0d --- /dev/null +++ b/examples/customTls/certs/ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIRANd+eeBjb4D4mNW86NmUFk8wDQYJKoZIhvcNAQELBQAw +KjESMBAGA1UEChMJbG9jYWxob3N0MRQwEgYDVQQDEwtQa2kgQ0EgUm9vdDAeFw0x +NzAyMjgyMjIzMDBaFw0yMDAyMTMyMjIzMDBaMCoxEjAQBgNVBAoTCWxvY2FsaG9z +dDEUMBIGA1UEAxMLUGtpIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDS4qFc21Bh5fw4UftWS/MLxKkyJklX+045brxmYL05zGA/isF1QWSq +pZaXaXhFr68/LcXAHOAiNzJSHe9ezscnn7lLN0J+6v5wvW6UKoQhMdCZpWHsGFe5 +e4od6hWJm6rjh3qGx4ENgqXOZNukRMYbig7MKGE5htxcnvdImrPXAiRtuJ6Aa6bl +dBhkpOhQwHEey90NtcliRM6H1jYcCbhtlRStCVXsWiMjfpq9YIq+Wf/ece27Rvgy +DX3UVNkRTuS0ZeX+D3n4lyOMTzgT6Cn0OUU23D5TRCCkDCDxkXgmnT6Cri9x2WnX +AT7c2apUAx6ms9+AACE32ijqSg0Zx0+zAgMBAAGjIzAhMA4GA1UdDwEB/wQEAwIC +pDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCZ/d2+NWcU2bNy +/W4XrwOHuBGVWW6vB2HGDN8l+Ut3K6Gbc5sXrkmmoap2y9zZKZl9mybchqQUJ9Qo +U8zrhRJ5L74NRay9Jm+csRXbMBdSZtfJ8RRzZK7cr+fZ3DTd7tReSmV00nj7ciGj +O2s73/GZHab7FzbTSbEf/5ei0UMAlN4L89DxzJxfnvIg6wu7dXg/QPhU3Ws4Y4bj +5Dpl7pS2ZnVTh+cz39PgD+WkjubSx/CfOoo0bvwXKvg7vuE3HB65aP8tEZePSj4t +MKWLAxwTNSqq7FVDrYkpgsnG00BTefaViTRyEuMaBWc4IpJ+r+W2ODEtFTWVyiyJ +zXOYmm2Y +-----END CERTIFICATE----- diff --git a/examples/customTls/certs/cert-key.pem b/examples/customTls/certs/cert-key.pem new file mode 100644 index 000000000..f1bcc858d --- /dev/null +++ b/examples/customTls/certs/cert-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAuV2YLWXBXUyRp0FEIBr/Jp2EeOcm9XfQPZzZ6M21eH3Cky29 ++OD0ynINBdFHi7QB5fFdnTtg4jpH+q9CvvpGVcrW6tpjKic+RaN6abnSkz+RrXII +X4RtfV/W9wMz6V/h8nhzGWQID0iQkhooaIvTie2nQ+gwQm8wmCL1+KT2IRoELCvC +3VwrjDGde+dSOp+g9oWo4CkCce+wnSQQkI1Htctaq9EayAnrpPFupSTw0+AEOBJK +fGXg2yBN3EqUXBmF/jNcNsMqjV4LCheH6QYWSUxt24HoL9ycPdRnPeUKmzPi9dE9 +dNfXvPtoltE8e/D8zTNNGWcRgGR0lzfxuWeW/wIDAQABAoIBAEx13+Sx+W3bvKTq +FgjMOf0asl8QshBEyL/xaC1QVQ+LiGwfTSJQ4Ih1PQvuRH3K1ZGc2wmVSaRnd/Ne +wcB3CfYvgjFDve3QXC5rfX4I6WRVr2iFBhEoVeWGV+xyBMK6C0ByEMAjc/Oh8ghi +A9MEAlD9l6Y6K1Xr+XZ3zVAv81q5ZMEQsTERUkLA9lwDUpkQVipoLoKEVUeDiRvB +jH+t9/I+axyARyuEx0Vx4Dza4AOhyNdW9J9szlAo2dhV21vW12MLvKH9jx1U2iAw +vOBPe87xX40EIbiUkDrFKogdibFylQp/EWdsWFPM794b3D8/czfh2QYInghfoTzO +bk5+rrECgYEA8u5UBYHYhHV9cQNAZVmpDE1JwUBg34q5M11TuSs7dyy2iqiHI7MS +ysGsaOlf2bycKXQw10Ut+SVR0qnxjf2E/+cuCwnMySCaCPzat9UcGTMk3PqW7B4r +foYmUmgH9n9zc845/L/LNEtHkeLEAGOJL+jvwx4dKxf0C84TZmHUKscCgYEAw1Z4 +QI5L6OKIL1dGDUWLMJXkplxDSPH7XNwDg6zGa81T1NfgDCA+lyLqXEp9YqMSOk5N +4X+mTspazmgv3x6b6urGtIIRENZFLFgKqNfwDFkWDShwChF/8M7bzJsS4P/cNtr1 +lV0RHFERErRIE88v4ErXWwzDmOC/fJojJEW3OgkCgYEAzEuVKVR7C1nq9kFvxEvU +mF3e6sADN7rn6MRRhmVPCvf1Q0Ja87DC2vRo04l/bBLrmQj3kfHBqcayuuDkHS7Y +zIRT+kBxkarzHx/Vp8d2a9LQ621pwoPUvACA9cg6+hdQtlD1/xIkB4RPWeZEQrdy +RXI1P/dxPC5WtB7HvdADpz0CgYEApzvkgABTZPJsfXtOchZT8CikNPlQcacZ+Io0 +SAsnZSvI1bRsEHWaoHI4CwOLDWNnO5vGeYR7sYD09TmlonPmMN0HeYrRaYTIfAp0 +NdGJpkiu5Fz2buhEjLnM3AL3ysHCmwQitNmUyJVu9IB8JNmAt5nbfgwTeVMRHXAp +HejB0WECgYEA5hLQLsyqJY+BzzrvsC9RJ5y/U+P1KMFWnTyo/O8q2tihqSc9tlmk +Jun18bc6z9qzwiSrYqOpAsE6IJlG+Cf39tXytVCxpuBXIe529VZekl2tEYaqdiQ1 +0fbh7R+eGKRjtl+bXucciv+jok13oNWCTuTcpzbxWbNxenpMVYPwB5s= +-----END RSA PRIVATE KEY----- diff --git a/examples/customTls/certs/cert.pem b/examples/customTls/certs/cert.pem new file mode 100644 index 000000000..709cd29c2 --- /dev/null +++ b/examples/customTls/certs/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIRAP5kAANG+3/2fVhI4QOB3qQwDQYJKoZIhvcNAQELBQAw +KjESMBAGA1UEChMJbG9jYWxob3N0MRQwEgYDVQQDEwtQa2kgQ0EgUm9vdDAeFw0x +NzAyMjgyMjIzMDBaFw0yMDAyMTMyMjIzMDBaMBExDzANBgNVBAoTBmNsaWVudDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALldmC1lwV1MkadBRCAa/yad +hHjnJvV30D2c2ejNtXh9wpMtvfjg9MpyDQXRR4u0AeXxXZ07YOI6R/qvQr76RlXK +1uraYyonPkWjemm50pM/ka1yCF+EbX1f1vcDM+lf4fJ4cxlkCA9IkJIaKGiL04nt +p0PoMEJvMJgi9fik9iEaBCwrwt1cK4wxnXvnUjqfoPaFqOApAnHvsJ0kEJCNR7XL +WqvRGsgJ66TxbqUk8NPgBDgSSnxl4NsgTdxKlFwZhf4zXDbDKo1eCwoXh+kGFklM +bduB6C/cnD3UZz3lCpsz4vXRPXTX17z7aJbRPHvw/M0zTRlnEYBkdJc38blnlv8C +AwEAAaNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr +BgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkq +hkiG9w0BAQsFAAOCAQEASZe43Hi5HDj4odD7fjPHZcNOFUjc4GV5xR0P5sKS1yVO +3D5Gf9v4OVA7O3ejCLQP2lSq++EMUChAVpCJsfzXDwqe8YoHLnSS+EID+JpHEZ8P +PXXc59S/2HN7kEP447MRJCpFbOHy/axFkzZAlpQr7ffUhPQy3gnDWHDQ6uie6Od5 +bRX2B6Whgy9ey7PR3CMT8+SowCjhsKqE3y0vto+55XxMec35uKXI/djOZzje94fE +n7plsWLOgYu0aeOkVKLe6s/Q0/mjO7zicDHwMq0QvFIcMU/MgrZkvcq+2sg/6Rp0 +iKnKss7cdrxnA7fOsGK8mWP0U6GizCvVigI1zN5dZg== +-----END CERTIFICATE----- diff --git a/examples/customTls/certs/server-cert.pem b/examples/customTls/certs/server-cert.pem new file mode 100644 index 000000000..024eb2546 --- /dev/null +++ b/examples/customTls/certs/server-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHTCCAgWgAwIBAgIQQcVnWwEK0r3ZCbXx+LQRtTANBgkqhkiG9w0BAQsFADAq +MRIwEAYDVQQKEwlsb2NhbGhvc3QxFDASBgNVBAMTC1BraSBDQSBSb290MB4XDTE3 +MDIyODIyMjMwMFoXDTIwMDIxMzIyMjMwMFowFDESMBAGA1UEChMJbG9jYWxob3N0 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOk54aC6EXZpNcxlO8L/ +ZIc/u4HNrB2VovT9osAs+ZPZOxd6w/l3e5mblxbh3j1Otjh5Y9e1wm9c0DytRhx9 +NUvzJ5weECGmk0FgU9Oe/OHnn5WayUK0Z8S56ln9bZbXDlnusRCyMGXhFuQqoabP +rpfqNvIP5XQeKxJMnLp2xYZV18O6nmjGYSSLMjNLr6MqA/KRRyP68yLhx4pXH1mL +bA5pjoxiJoI7tGLUTkMWco4O7bW0czR9AK72ZALSDgWDIKLQlRlQJ05wk+wnDykk +4pt6gABodkeyJvp6D5ayuAZBH0xOGxOW2Y4RPowb0tNIw4Sd7HthodLLjUiM7QwJ +AQIDAQABo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIG +CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0G +CSqGSIb3DQEBCwUAA4IBAQB3DOgS/faqoMLqHCwTJsocD4etXU1Es7zTsI8QSYLp +JCj4ykJ58NASmfwtn+MDBc0wiKqP8Cyudh5j2uQGQ/pL/h99EGy4TY7rN/riop0m +x07yvV+mi65nwT/YEyX3Jymx4+78AvdHIWT9uj5nED6+pBufqHQHVm6btPF/hsA1 +4YTbWSGnrkcplBw3sWP5HoVVtfJ7TzhqVrSSQB+lRRXPHzRCq5f3BM5fZ7bMt3LI +9j8cXrSXSu53LZ/llTLGU5DbReAJPDQpSdwy56wMD/cXQXxt6blwZdLEneYQ8eqO +kD7eZDlUKz1Dhj7qoFnAhDU1ypxBF4A04E/qpPQHrkq1 +-----END CERTIFICATE----- diff --git a/examples/customTls/certs/server-key.pem b/examples/customTls/certs/server-key.pem new file mode 100644 index 000000000..7366917d5 --- /dev/null +++ b/examples/customTls/certs/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAwOk54aC6EXZpNcxlO8L/ZIc/u4HNrB2VovT9osAs+ZPZOxd6 +w/l3e5mblxbh3j1Otjh5Y9e1wm9c0DytRhx9NUvzJ5weECGmk0FgU9Oe/OHnn5Wa +yUK0Z8S56ln9bZbXDlnusRCyMGXhFuQqoabPrpfqNvIP5XQeKxJMnLp2xYZV18O6 +nmjGYSSLMjNLr6MqA/KRRyP68yLhx4pXH1mLbA5pjoxiJoI7tGLUTkMWco4O7bW0 +czR9AK72ZALSDgWDIKLQlRlQJ05wk+wnDykk4pt6gABodkeyJvp6D5ayuAZBH0xO +GxOW2Y4RPowb0tNIw4Sd7HthodLLjUiM7QwJAQIDAQABAoIBAG9edc7/ZkwsiyLG +5G6y7ZRQzIdosZ862SdhvofV4GEZbODDdllrTQJrNLruN+mAhU+HnPT6FHGyrud9 +EB+Y1OQO+8qTQ3vWoX8D0CO02WVu2bR0vw6P1uzNUvOrjjZVTcR4QOeyFt0ABAme +Icp/LgjCpTGhX3H9Lml5QAd/UpBHMuGYpZ7NZTRED4spWKYoQeqhCNzU7lYfW9W4 +xPdRbgBJ75daUPFCrCAU/hny+Iosl8Qc+dl9F+idtI4bvWw+ozKzgwhgicrYtYiQ +yuyR8DFD/hQ8Kl5Gzxmdkz97MZu2MffXiJaa7MelrAKV9L8plVmzPX3nNkphHjM5 +aCDDlUECgYEA82wrxpWwBLzgxSQGpN1wV3p29W8+tjp5XsTGQvHdFEPPLqAvl4oX +fZdLNSyiTI1lMbQ1zy6K9jQABIn4KEWKj6xNXXnj3tQsjJLbJBuC+2UaKJa2CG01 +jESTTLmjgBIGZneRxgYdFF5xyINnnaydhH0ijVXNIfrP2r4W4mqGRGkCgYEAyuDs +TKzgfNTbotJ4U4u44Y1TjmEknNggjNktOKPn6uwi5OITXMcdSzmQUPRvnX6Fm/Iy +6qF0XlG3eBFYxNWnWRLzE8xMFovnhBBlgTowF9X3KjgWmkoj9EIPLuzofjpPQEpU +wgbhAsO8beqbmmQCraIb1j4UOJdaCBN5gCpqLNkCgYEA0couSC4Fz2+BQCZ2W2xF +P/9ZutkvcRogNB7eyB9u6+ItEwAXREFNUX4s4R0gm11ZE2c+4No6BUp3oXHPH9Yf +Pwe+fYtpakfuRRDkMNBNKKDP1J9fLxAAEG2hjYSIdv4R6gmu5r2qHj3vTmKB0JBO +CTtXpfuGmXxx2xHs4yHvqdECgYAS8f4fCuXLwnTgN5dU9e9F4NS0rw2kN+qSPJWk +fGnj3jlD9nioaU+q/q0jYjAqHO6NKYjnsDwVsrvXUodfmjQOdV6Nsr5IPLhZ9M4F +y1FCaJC0OJijv9irrp+MWkM7xmYwMsDHfaz1fSHTgd0WBdDaNhEzaIiq4DdgGbF2 +7n8LwQKBgEvx0KL7fxljpu/UC7m21p7o6S2ZVNr8aTiaTqiNeYrfiCCt1sbYB/to +dHs1sgnUfRT7BfVpDgGTR0m/1IHp7jjvtQRkpCR/EyJxe8nYs/VERV4XhZb08tzo +3AlVnxWkYZ2WhzRSRbRXwuF+7xVSh/XDycXX5I48Xztz/8ZNq/W3 +-----END RSA PRIVATE KEY----- diff --git a/examples/customTls/self_signed_certificate_test.go b/examples/customTls/self_signed_certificate_test.go new file mode 100644 index 000000000..4f7177dcf --- /dev/null +++ b/examples/customTls/self_signed_certificate_test.go @@ -0,0 +1,86 @@ +package provider + +import ( + "fmt" + "os" + "github.com/pact-foundation/pact-go/dsl" + "github.com/pact-foundation/pact-go/utils" + "path/filepath" + "testing" + "crypto/tls" + "crypto/x509" + "github.com/pact-foundation/pact-go/types" + "io/ioutil" + "log" + "net/http" +) + +// An external HTTPS provider +func TestPact_SelfSignedTLSProvider(t *testing.T) { + go startServer() + + pact := createPact() + _, err := pact.VerifyProvider(t, types.VerifyRequest{ + ProviderBaseURL: fmt.Sprintf("https://localhost:%d", port), + PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/consumer-selfsignedtls.json", pactDir))}, + CustomTLSConfig: &tls.Config{ + RootCAs: getCaCertPool(), + // InsecureSkipVerify: true, // Disable SSL verification altogether + }, + }) + + if err != nil { + t.Fatal(err) + } +} + +func HelloServer(w http.ResponseWriter, req *http.Request) { } + +func startServer() { + http.HandleFunc("/hello", HelloServer) + + // Setup HTTPS client + tlsConfig := &tls.Config{ + ClientCAs: getCaCertPool(), + ClientAuth: tls.NoClientCert, + } + tlsConfig.BuildNameToCertificate() + + server := &http.Server{ + Addr: fmt.Sprintf(":%d", port), + TLSConfig: tlsConfig, + } + + log.Fatalf("%v", server.ListenAndServeTLS("certs/server-cert.pem", "certs/server-key.pem")) +} + +// Configuration / Test Data +var dir, _ = os.Getwd() +var pactDir = fmt.Sprintf("%s/../pacts", dir) +var logDir = fmt.Sprintf("%s/log", dir) +var port, _ = utils.GetFreePort() + +// Setup the Pact client. +func createPact() dsl.Pact { + return dsl.Pact{ + Consumer: "consumer", + Provider: "selfsignedtls", + LogDir: logDir, + PactDir: pactDir, + DisableToolValidityCheck: true, + LogLevel: "DEBUG", + } +} + +// Custom certificate authority +func getCaCertPool() *x509.CertPool { + caCert, err := ioutil.ReadFile("certs/ca.pem") + if err != nil { + log.Fatal(err) + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + return caCertPool +} \ No newline at end of file diff --git a/examples/httpbin/httpbin_test.go b/examples/httpbin/httpbin_test.go index 6d1d46e83..d81b88787 100644 --- a/examples/httpbin/httpbin_test.go +++ b/examples/httpbin/httpbin_test.go @@ -2,21 +2,18 @@ package provider import ( "fmt" - "os" "path/filepath" "testing" - + "os" "github.com/pact-foundation/pact-go/dsl" - "github.com/pact-foundation/pact-go/types" "github.com/pact-foundation/pact-go/utils" + "github.com/pact-foundation/pact-go/types" ) // An external HTTPS provider -func TestPact_GinProvider(t *testing.T) { - +func TestPact_ExternalHttpsProvider(t *testing.T) { pact := createPact() - // Verify the Provider with local Pact Files _, err := pact.VerifyProvider(t, types.VerifyRequest{ ProviderBaseURL: "https://httpbin.org", PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/consumer-httpbin.json", pactDir))}, @@ -37,11 +34,11 @@ var port, _ = utils.GetFreePort() // Setup the Pact client. func createPact() dsl.Pact { return dsl.Pact{ - Consumer: "jmarie", - Provider: "loginprovider", + Consumer: "consumer", + Provider: "httpbinprovider", LogDir: logDir, PactDir: pactDir, DisableToolValidityCheck: true, LogLevel: "DEBUG", } -} +} \ No newline at end of file diff --git a/examples/mux/provider/user_service_test.go b/examples/mux/provider/user_service_test.go index a759caeae..d262f2d77 100644 --- a/examples/mux/provider/user_service_test.go +++ b/examples/mux/provider/user_service_test.go @@ -19,7 +19,7 @@ import ( // The Provider verification func TestPact_MuxProvider(t *testing.T) { - go startInstrumentedProvider() + startProvider() pact := createPact() @@ -129,7 +129,7 @@ var stateHandlers = types.StateHandlers{ // Starts the provider API with hooks for provider states. // This essentially mirrors the main.go file, with extra routes added. -func startInstrumentedProvider() { +func startProvider() { mux := http.NewServeMux() mux.HandleFunc("/users/", IsAuthenticated(GetUser)) mux.HandleFunc("/login/", UserLogin) diff --git a/examples/pacts/consumer-selfsignedtls.json b/examples/pacts/consumer-selfsignedtls.json new file mode 100644 index 000000000..36cef42e4 --- /dev/null +++ b/examples/pacts/consumer-selfsignedtls.json @@ -0,0 +1,25 @@ +{ + "consumer": { + "name": "consumer" + }, + "provider": { + "name": "httpbin" + }, + "interactions": [ + { + "description": "A request to the hello self-signed API", + "request": { + "method": "GET", + "path": "/hello" + }, + "response": { + "status": 200 + } + } + ], + "metadata": { + "pactSpecification": { + "version": "2.0.0" + } + } +} diff --git a/proxy/http.go b/proxy/http.go index 84a1260d3..909c70a9a 100644 --- a/proxy/http.go +++ b/proxy/http.go @@ -41,6 +41,10 @@ type Options struct { // Internal request prefix for proxy to not rewrite InternalRequestPathPrefix string + + // Custom TLS Configuration for communicating with a Provider + // Useful when verifying self-signed services, MASSL etc. + CustomTLSConfig *tls.Config } // loggingMiddleware logs requests to the proxy @@ -78,8 +82,11 @@ func HTTPReverseProxy(options Options) (int, error) { Host: options.TargetAddress, Path: options.TargetPath, } + + // TODO: may be able to revert to the default single proxy + // and just override the transport! proxy := createProxy(url, options.InternalRequestPathPrefix) - proxy.Transport = debugTransport{} + proxy.Transport = customTransport{tlsConfig: options.CustomTLSConfig} if port == 0 { port, err = utils.GetFreePort() @@ -100,31 +107,36 @@ func HTTPReverseProxy(options Options) (int, error) { // https://stackoverflow.com/questions/52986853/how-to-debug-httputil-newsinglehostreverseproxy // Set the proxy.Transport field to an implementation that dumps the request before delegating to the default transport: -type debugTransport struct{} +type customTransport struct { + tlsConfig *tls.Config +} -func (debugTransport) RoundTrip(r *http.Request) (*http.Response, error) { +func (c customTransport) RoundTrip(r *http.Request) (*http.Response, error) { b, err := httputil.DumpRequestOut(r, false) if err != nil { return nil, err } log.Println("[TRACE] proxy outgoing request\n", string(b)) - var DefaultTransport http.RoundTripper = &http.Transport{ + transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } + if c.tlsConfig != nil { + log.Println("[DEBUG] applying custom TLS config") + transport.TLSClientConfig = c.tlsConfig + } + var DefaultTransport http.RoundTripper = transport + res, err := DefaultTransport.RoundTrip(r) b, err = httputil.DumpResponse(res, false) log.Println("[TRACE] proxied server response\n", string(b)) diff --git a/scripts/pact.ps1 b/scripts/pact.ps1 index 62a16c72d..7b714da9d 100644 --- a/scripts/pact.ps1 +++ b/scripts/pact.ps1 @@ -70,7 +70,7 @@ foreach ($package in $packages) { Write-Host "--> Testing E2E examples" $env:PACT_INTEGRATED_TESTS=1 -$examples=@("github.com/pact-foundation/pact-go/examples/consumer/goconsumer", "github.com/pact-foundation/pact-go/examples/go-kit/provider", "github.com/pact-foundation/pact-go/examples/mux/provider", "github.com/pact-foundation/pact-go/examples/gin/provider") +$examples=@("github.com/pact-foundation/pact-go/examples/consumer/goconsumer", "github.com/pact-foundation/pact-go/examples/go-kit/provider", "github.com/pact-foundation/pact-go/examples/mux/provider", "github.com/pact-foundation/pact-go/examples/gin/provider", "github.com/pact-foundation/pact-go/examples/httpbin", "github.com/pact-foundation/pact-go/examples/customTls") foreach ($example in $examples) { Write-Host "Installing dependencies for example: $example" cd "$env:GOPATH\src\$example" diff --git a/scripts/pact.sh b/scripts/pact.sh index 0d955ef55..13d1fe46f 100755 --- a/scripts/pact.sh +++ b/scripts/pact.sh @@ -31,7 +31,7 @@ export PACT_BROKER_PASSWORD="O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1" export PATH="../build/pact/bin:${PATH}" step "Running E2E regression and example projects" -examples=("github.com/pact-foundation/pact-go/examples/consumer/goconsumer" "github.com/pact-foundation/pact-go/examples/go-kit/provider" "github.com/pact-foundation/pact-go/examples/mux/provider" "github.com/pact-foundation/pact-go/examples/gin/provider" "github.com/pact-foundation/pact-go/examples/messages/consumer" "github.com/pact-foundation/pact-go/examples/messages/provider" "github.com/pact-foundation/pact-go/examples/httpbin") +examples=("github.com/pact-foundation/pact-go/examples/consumer/goconsumer" "github.com/pact-foundation/pact-go/examples/go-kit/provider" "github.com/pact-foundation/pact-go/examples/mux/provider" "github.com/pact-foundation/pact-go/examples/gin/provider" "github.com/pact-foundation/pact-go/examples/messages/consumer" "github.com/pact-foundation/pact-go/examples/messages/provider" "github.com/pact-foundation/pact-go/examples/httpbin" "github.com/pact-foundation/pact-go/examples/customTls") for example in "${examples[@]}" do diff --git a/types/verify_request.go b/types/verify_request.go index 8a7b79956..d65c50401 100644 --- a/types/verify_request.go +++ b/types/verify_request.go @@ -1,6 +1,7 @@ package types import ( + "crypto/tls" "errors" "fmt" "log" @@ -84,6 +85,10 @@ type VerifyRequest struct { // runs the risk of changing the contract and breaking the real system. RequestFilter proxy.Middleware + // Custom TLS Configuration to use when making the requests to/from + // the Provider API. Useful for setting custom certificates, MASSL etc. + CustomTLSConfig *tls.Config + // Verbose increases verbosity of output // Deprecated Verbose bool diff --git a/vendor/github.com/go-kit/kit/LICENSE b/vendor/github.com/go-kit/kit/LICENSE deleted file mode 100644 index 9d83342ac..000000000 --- a/vendor/github.com/go-kit/kit/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Peter Bourgon - -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/go-kit/kit/endpoint/doc.go b/vendor/github.com/go-kit/kit/endpoint/doc.go deleted file mode 100644 index 84e27b95d..000000000 --- a/vendor/github.com/go-kit/kit/endpoint/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package endpoint defines an abstraction for RPCs. -// -// Endpoints are a fundamental building block for many Go kit components. -// Endpoints are implemented by servers, and called by clients. -package endpoint diff --git a/vendor/github.com/go-kit/kit/endpoint/endpoint.go b/vendor/github.com/go-kit/kit/endpoint/endpoint.go deleted file mode 100644 index 1b64f50ed..000000000 --- a/vendor/github.com/go-kit/kit/endpoint/endpoint.go +++ /dev/null @@ -1,28 +0,0 @@ -package endpoint - -import ( - "context" -) - -// Endpoint is the fundamental building block of servers and clients. -// It represents a single RPC method. -type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error) - -// Nop is an endpoint that does nothing and returns a nil error. -// Useful for tests. -func Nop(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil } - -// Middleware is a chainable behavior modifier for endpoints. -type Middleware func(Endpoint) Endpoint - -// Chain is a helper function for composing middlewares. Requests will -// traverse them in the order they're declared. That is, the first middleware -// is treated as the outermost middleware. -func Chain(outer Middleware, others ...Middleware) Middleware { - return func(next Endpoint) Endpoint { - for i := len(others) - 1; i >= 0; i-- { // reverse - next = others[i](next) - } - return outer(next) - } -} diff --git a/vendor/github.com/go-kit/kit/log/README.md b/vendor/github.com/go-kit/kit/log/README.md deleted file mode 100644 index 7222f8009..000000000 --- a/vendor/github.com/go-kit/kit/log/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# package log - -`package log` provides a minimal interface for structured logging in services. -It may be wrapped to encode conventions, enforce type-safety, provide leveled -logging, and so on. It can be used for both typical application log events, -and log-structured data streams. - -## Structured logging - -Structured logging is, basically, conceding to the reality that logs are -_data_, and warrant some level of schematic rigor. Using a stricter, -key/value-oriented message format for our logs, containing contextual and -semantic information, makes it much easier to get insight into the -operational activity of the systems we build. Consequently, `package log` is -of the strong belief that "[the benefits of structured logging outweigh the -minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)". - -Migrating from unstructured to structured logging is probably a lot easier -than you'd expect. - -```go -// Unstructured -log.Printf("HTTP server listening on %s", addr) - -// Structured -logger.Log("transport", "HTTP", "addr", addr, "msg", "listening") -``` - -## Usage - -### Typical application logging - -```go -w := log.NewSyncWriter(os.Stderr) -logger := log.NewLogfmtLogger(w) -logger.Log("question", "what is the meaning of life?", "answer", 42) - -// Output: -// question="what is the meaning of life?" answer=42 -``` - -### Contextual Loggers - -```go -func main() { - var logger log.Logger - logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) - logger = log.With(logger, "instance_id", 123) - - logger.Log("msg", "starting") - NewWorker(log.With(logger, "component", "worker")).Run() - NewSlacker(log.With(logger, "component", "slacker")).Run() -} - -// Output: -// instance_id=123 msg=starting -// instance_id=123 component=worker msg=running -// instance_id=123 component=slacker msg=running -``` - -### Interact with stdlib logger - -Redirect stdlib logger to Go kit logger. - -```go -import ( - "os" - stdlog "log" - kitlog "github.com/go-kit/kit/log" -) - -func main() { - logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout)) - stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) - stdlog.Print("I sure like pie") -} - -// Output: -// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"} -``` - -Or, if, for legacy reasons, you need to pipe all of your logging through the -stdlib log package, you can redirect Go kit logger to the stdlib logger. - -```go -logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{}) -logger.Log("legacy", true, "msg", "at least it's something") - -// Output: -// 2016/01/01 12:34:56 legacy=true msg="at least it's something" -``` - -### Timestamps and callers - -```go -var logger log.Logger -logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) -logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) - -logger.Log("msg", "hello") - -// Output: -// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello -``` - -## Supported output formats - -- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write)) -- JSON - -## Enhancements - -`package log` is centered on the one-method Logger interface. - -```go -type Logger interface { - Log(keyvals ...interface{}) error -} -``` - -This interface, and its supporting code like is the product of much iteration -and evaluation. For more details on the evolution of the Logger interface, -see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1), -a talk by [Chris Hines](https://github.com/ChrisHines). -Also, please see -[#63](https://github.com/go-kit/kit/issues/63), -[#76](https://github.com/go-kit/kit/pull/76), -[#131](https://github.com/go-kit/kit/issues/131), -[#157](https://github.com/go-kit/kit/pull/157), -[#164](https://github.com/go-kit/kit/issues/164), and -[#252](https://github.com/go-kit/kit/pull/252) -to review historical conversations about package log and the Logger interface. - -Value-add packages and suggestions, -like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/level), -are of course welcome. Good proposals should - -- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/kit/log#With), -- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped contextual loggers, and -- Be friendly to packages that accept only an unadorned log.Logger. - -## Benchmarks & comparisons - -There are a few Go logging benchmarks and comparisons that include Go kit's package log. - -- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log -- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log diff --git a/vendor/github.com/go-kit/kit/log/doc.go b/vendor/github.com/go-kit/kit/log/doc.go deleted file mode 100644 index 918c0af46..000000000 --- a/vendor/github.com/go-kit/kit/log/doc.go +++ /dev/null @@ -1,116 +0,0 @@ -// Package log provides a structured logger. -// -// Structured logging produces logs easily consumed later by humans or -// machines. Humans might be interested in debugging errors, or tracing -// specific requests. Machines might be interested in counting interesting -// events, or aggregating information for off-line processing. In both cases, -// it is important that the log messages are structured and actionable. -// Package log is designed to encourage both of these best practices. -// -// Basic Usage -// -// The fundamental interface is Logger. Loggers create log events from -// key/value data. The Logger interface has a single method, Log, which -// accepts a sequence of alternating key/value pairs, which this package names -// keyvals. -// -// type Logger interface { -// Log(keyvals ...interface{}) error -// } -// -// Here is an example of a function using a Logger to create log events. -// -// func RunTask(task Task, logger log.Logger) string { -// logger.Log("taskID", task.ID, "event", "starting task") -// ... -// logger.Log("taskID", task.ID, "event", "task complete") -// } -// -// The keys in the above example are "taskID" and "event". The values are -// task.ID, "starting task", and "task complete". Every key is followed -// immediately by its value. -// -// Keys are usually plain strings. Values may be any type that has a sensible -// encoding in the chosen log format. With structured logging it is a good -// idea to log simple values without formatting them. This practice allows -// the chosen logger to encode values in the most appropriate way. -// -// Contextual Loggers -// -// A contextual logger stores keyvals that it includes in all log events. -// Building appropriate contextual loggers reduces repetition and aids -// consistency in the resulting log output. With and WithPrefix add context to -// a logger. We can use With to improve the RunTask example. -// -// func RunTask(task Task, logger log.Logger) string { -// logger = log.With(logger, "taskID", task.ID) -// logger.Log("event", "starting task") -// ... -// taskHelper(task.Cmd, logger) -// ... -// logger.Log("event", "task complete") -// } -// -// The improved version emits the same log events as the original for the -// first and last calls to Log. Passing the contextual logger to taskHelper -// enables each log event created by taskHelper to include the task.ID even -// though taskHelper does not have access to that value. Using contextual -// loggers this way simplifies producing log output that enables tracing the -// life cycle of individual tasks. (See the Contextual example for the full -// code of the above snippet.) -// -// Dynamic Contextual Values -// -// A Valuer function stored in a contextual logger generates a new value each -// time an event is logged. The Valuer example demonstrates how this feature -// works. -// -// Valuers provide the basis for consistently logging timestamps and source -// code location. The log package defines several valuers for that purpose. -// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and -// DefaultCaller. A common logger initialization sequence that ensures all log -// entries contain a timestamp and source location looks like this: -// -// logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) -// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) -// -// Concurrent Safety -// -// Applications with multiple goroutines want each log event written to the -// same logger to remain separate from other log events. Package log provides -// two simple solutions for concurrent safe logging. -// -// NewSyncWriter wraps an io.Writer and serializes each call to its Write -// method. Using a SyncWriter has the benefit that the smallest practical -// portion of the logging logic is performed within a mutex, but it requires -// the formatting Logger to make only one call to Write per log event. -// -// NewSyncLogger wraps any Logger and serializes each call to its Log method. -// Using a SyncLogger has the benefit that it guarantees each log event is -// handled atomically within the wrapped logger, but it typically serializes -// both the formatting and output logic. Use a SyncLogger if the formatting -// logger may perform multiple writes per log event. -// -// Error Handling -// -// This package relies on the practice of wrapping or decorating loggers with -// other loggers to provide composable pieces of functionality. It also means -// that Logger.Log must return an error because some -// implementations—especially those that output log data to an io.Writer—may -// encounter errors that cannot be handled locally. This in turn means that -// Loggers that wrap other loggers should return errors from the wrapped -// logger up the stack. -// -// Fortunately, the decorator pattern also provides a way to avoid the -// necessity to check for errors every time an application calls Logger.Log. -// An application required to panic whenever its Logger encounters -// an error could initialize its logger as follows. -// -// fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) -// logger := log.LoggerFunc(func(keyvals ...interface{}) error { -// if err := fmtlogger.Log(keyvals...); err != nil { -// panic(err) -// } -// return nil -// }) -package log diff --git a/vendor/github.com/go-kit/kit/log/json_logger.go b/vendor/github.com/go-kit/kit/log/json_logger.go deleted file mode 100644 index 66094b4dd..000000000 --- a/vendor/github.com/go-kit/kit/log/json_logger.go +++ /dev/null @@ -1,89 +0,0 @@ -package log - -import ( - "encoding" - "encoding/json" - "fmt" - "io" - "reflect" -) - -type jsonLogger struct { - io.Writer -} - -// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a -// single JSON object. Each log event produces no more than one call to -// w.Write. The passed Writer must be safe for concurrent use by multiple -// goroutines if the returned Logger will be used concurrently. -func NewJSONLogger(w io.Writer) Logger { - return &jsonLogger{w} -} - -func (l *jsonLogger) Log(keyvals ...interface{}) error { - n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd - m := make(map[string]interface{}, n) - for i := 0; i < len(keyvals); i += 2 { - k := keyvals[i] - var v interface{} = ErrMissingValue - if i+1 < len(keyvals) { - v = keyvals[i+1] - } - merge(m, k, v) - } - return json.NewEncoder(l.Writer).Encode(m) -} - -func merge(dst map[string]interface{}, k, v interface{}) { - var key string - switch x := k.(type) { - case string: - key = x - case fmt.Stringer: - key = safeString(x) - default: - key = fmt.Sprint(x) - } - - // We want json.Marshaler and encoding.TextMarshaller to take priority over - // err.Error() and v.String(). But json.Marshall (called later) does that by - // default so we force a no-op if it's one of those 2 case. - switch x := v.(type) { - case json.Marshaler: - case encoding.TextMarshaler: - case error: - v = safeError(x) - case fmt.Stringer: - v = safeString(x) - } - - dst[key] = v -} - -func safeString(str fmt.Stringer) (s string) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { - s = "NULL" - } else { - panic(panicVal) - } - } - }() - s = str.String() - return -} - -func safeError(err error) (s interface{}) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { - s = nil - } else { - panic(panicVal) - } - } - }() - s = err.Error() - return -} diff --git a/vendor/github.com/go-kit/kit/log/log.go b/vendor/github.com/go-kit/kit/log/log.go deleted file mode 100644 index 66a9e2fde..000000000 --- a/vendor/github.com/go-kit/kit/log/log.go +++ /dev/null @@ -1,135 +0,0 @@ -package log - -import "errors" - -// Logger is the fundamental interface for all log operations. Log creates a -// log event from keyvals, a variadic sequence of alternating keys and values. -// Implementations must be safe for concurrent use by multiple goroutines. In -// particular, any implementation of Logger that appends to keyvals or -// modifies or retains any of its elements must make a copy first. -type Logger interface { - Log(keyvals ...interface{}) error -} - -// ErrMissingValue is appended to keyvals slices with odd length to substitute -// the missing value. -var ErrMissingValue = errors.New("(MISSING)") - -// With returns a new contextual logger with keyvals prepended to those passed -// to calls to Log. If logger is also a contextual logger created by With or -// WithPrefix, keyvals is appended to the existing context. -// -// The returned Logger replaces all value elements (odd indexes) containing a -// Valuer with their generated value for each call to its Log method. -func With(logger Logger, keyvals ...interface{}) Logger { - if len(keyvals) == 0 { - return logger - } - l := newContext(logger) - kvs := append(l.keyvals, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - return &context{ - logger: l.logger, - // Limiting the capacity of the stored keyvals ensures that a new - // backing array is created if the slice must grow in Log or With. - // Using the extra capacity without copying risks a data race that - // would violate the Logger interface contract. - keyvals: kvs[:len(kvs):len(kvs)], - hasValuer: l.hasValuer || containsValuer(keyvals), - } -} - -// WithPrefix returns a new contextual logger with keyvals prepended to those -// passed to calls to Log. If logger is also a contextual logger created by -// With or WithPrefix, keyvals is prepended to the existing context. -// -// The returned Logger replaces all value elements (odd indexes) containing a -// Valuer with their generated value for each call to its Log method. -func WithPrefix(logger Logger, keyvals ...interface{}) Logger { - if len(keyvals) == 0 { - return logger - } - l := newContext(logger) - // Limiting the capacity of the stored keyvals ensures that a new - // backing array is created if the slice must grow in Log or With. - // Using the extra capacity without copying risks a data race that - // would violate the Logger interface contract. - n := len(l.keyvals) + len(keyvals) - if len(keyvals)%2 != 0 { - n++ - } - kvs := make([]interface{}, 0, n) - kvs = append(kvs, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - kvs = append(kvs, l.keyvals...) - return &context{ - logger: l.logger, - keyvals: kvs, - hasValuer: l.hasValuer || containsValuer(keyvals), - } -} - -// context is the Logger implementation returned by With and WithPrefix. It -// wraps a Logger and holds keyvals that it includes in all log events. Its -// Log method calls bindValues to generate values for each Valuer in the -// context keyvals. -// -// A context must always have the same number of stack frames between calls to -// its Log method and the eventual binding of Valuers to their value. This -// requirement comes from the functional requirement to allow a context to -// resolve application call site information for a Caller stored in the -// context. To do this we must be able to predict the number of logging -// functions on the stack when bindValues is called. -// -// Two implementation details provide the needed stack depth consistency. -// -// 1. newContext avoids introducing an additional layer when asked to -// wrap another context. -// 2. With and WithPrefix avoid introducing an additional layer by -// returning a newly constructed context with a merged keyvals rather -// than simply wrapping the existing context. -type context struct { - logger Logger - keyvals []interface{} - hasValuer bool -} - -func newContext(logger Logger) *context { - if c, ok := logger.(*context); ok { - return c - } - return &context{logger: logger} -} - -// Log replaces all value elements (odd indexes) containing a Valuer in the -// stored context with their generated value, appends keyvals, and passes the -// result to the wrapped Logger. -func (l *context) Log(keyvals ...interface{}) error { - kvs := append(l.keyvals, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - if l.hasValuer { - // If no keyvals were appended above then we must copy l.keyvals so - // that future log events will reevaluate the stored Valuers. - if len(keyvals) == 0 { - kvs = append([]interface{}{}, l.keyvals...) - } - bindValues(kvs[:len(l.keyvals)]) - } - return l.logger.Log(kvs...) -} - -// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If -// f is a function with the appropriate signature, LoggerFunc(f) is a Logger -// object that calls f. -type LoggerFunc func(...interface{}) error - -// Log implements Logger by calling f(keyvals...). -func (f LoggerFunc) Log(keyvals ...interface{}) error { - return f(keyvals...) -} diff --git a/vendor/github.com/go-kit/kit/log/logfmt_logger.go b/vendor/github.com/go-kit/kit/log/logfmt_logger.go deleted file mode 100644 index a00305298..000000000 --- a/vendor/github.com/go-kit/kit/log/logfmt_logger.go +++ /dev/null @@ -1,62 +0,0 @@ -package log - -import ( - "bytes" - "io" - "sync" - - "github.com/go-logfmt/logfmt" -) - -type logfmtEncoder struct { - *logfmt.Encoder - buf bytes.Buffer -} - -func (l *logfmtEncoder) Reset() { - l.Encoder.Reset() - l.buf.Reset() -} - -var logfmtEncoderPool = sync.Pool{ - New: func() interface{} { - var enc logfmtEncoder - enc.Encoder = logfmt.NewEncoder(&enc.buf) - return &enc - }, -} - -type logfmtLogger struct { - w io.Writer -} - -// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in -// logfmt format. Each log event produces no more than one call to w.Write. -// The passed Writer must be safe for concurrent use by multiple goroutines if -// the returned Logger will be used concurrently. -func NewLogfmtLogger(w io.Writer) Logger { - return &logfmtLogger{w} -} - -func (l logfmtLogger) Log(keyvals ...interface{}) error { - enc := logfmtEncoderPool.Get().(*logfmtEncoder) - enc.Reset() - defer logfmtEncoderPool.Put(enc) - - if err := enc.EncodeKeyvals(keyvals...); err != nil { - return err - } - - // Add newline to the end of the buffer - if err := enc.EndRecord(); err != nil { - return err - } - - // The Logger interface requires implementations to be safe for concurrent - // use by multiple goroutines. For this implementation that means making - // only one call to l.w.Write() for each call to Log. - if _, err := l.w.Write(enc.buf.Bytes()); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/go-kit/kit/log/nop_logger.go b/vendor/github.com/go-kit/kit/log/nop_logger.go deleted file mode 100644 index 1047d626c..000000000 --- a/vendor/github.com/go-kit/kit/log/nop_logger.go +++ /dev/null @@ -1,8 +0,0 @@ -package log - -type nopLogger struct{} - -// NewNopLogger returns a logger that doesn't do anything. -func NewNopLogger() Logger { return nopLogger{} } - -func (nopLogger) Log(...interface{}) error { return nil } diff --git a/vendor/github.com/go-kit/kit/log/stdlib.go b/vendor/github.com/go-kit/kit/log/stdlib.go deleted file mode 100644 index ff96b5dee..000000000 --- a/vendor/github.com/go-kit/kit/log/stdlib.go +++ /dev/null @@ -1,116 +0,0 @@ -package log - -import ( - "io" - "log" - "regexp" - "strings" -) - -// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's -// designed to be passed to a Go kit logger as the writer, for cases where -// it's necessary to redirect all Go kit log output to the stdlib logger. -// -// If you have any choice in the matter, you shouldn't use this. Prefer to -// redirect the stdlib log to the Go kit logger via NewStdlibAdapter. -type StdlibWriter struct{} - -// Write implements io.Writer. -func (w StdlibWriter) Write(p []byte) (int, error) { - log.Print(strings.TrimSpace(string(p))) - return len(p), nil -} - -// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib -// logger's SetOutput. It will extract date/timestamps, filenames, and -// messages, and place them under relevant keys. -type StdlibAdapter struct { - Logger - timestampKey string - fileKey string - messageKey string -} - -// StdlibAdapterOption sets a parameter for the StdlibAdapter. -type StdlibAdapterOption func(*StdlibAdapter) - -// TimestampKey sets the key for the timestamp field. By default, it's "ts". -func TimestampKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.timestampKey = key } -} - -// FileKey sets the key for the file and line field. By default, it's "caller". -func FileKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.fileKey = key } -} - -// MessageKey sets the key for the actual log message. By default, it's "msg". -func MessageKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.messageKey = key } -} - -// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed -// logger. It's designed to be passed to log.SetOutput. -func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { - a := StdlibAdapter{ - Logger: logger, - timestampKey: "ts", - fileKey: "caller", - messageKey: "msg", - } - for _, option := range options { - option(&a) - } - return a -} - -func (a StdlibAdapter) Write(p []byte) (int, error) { - result := subexps(p) - keyvals := []interface{}{} - var timestamp string - if date, ok := result["date"]; ok && date != "" { - timestamp = date - } - if time, ok := result["time"]; ok && time != "" { - if timestamp != "" { - timestamp += " " - } - timestamp += time - } - if timestamp != "" { - keyvals = append(keyvals, a.timestampKey, timestamp) - } - if file, ok := result["file"]; ok && file != "" { - keyvals = append(keyvals, a.fileKey, file) - } - if msg, ok := result["msg"]; ok { - keyvals = append(keyvals, a.messageKey, msg) - } - if err := a.Logger.Log(keyvals...); err != nil { - return 0, err - } - return len(p), nil -} - -const ( - logRegexpDate = `(?P[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` - logRegexpTime = `(?P