From 1fa10d02e10edf59e7b0c25afc45b180fc8ef024 Mon Sep 17 00:00:00 2001 From: islishude Date: Wed, 24 Jun 2020 19:07:56 +0800 Subject: [PATCH 1/8] refector wait.HTTPStrategy --- wait/http.go | 96 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/wait/http.go b/wait/http.go index e778eb4296..5e7a96804b 100644 --- a/wait/http.go +++ b/wait/http.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "errors" "fmt" + "io" "net" "net/http" "strconv" @@ -26,6 +27,9 @@ type HTTPStrategy struct { StatusCodeMatcher func(status int) bool UseTLS bool AllowInsecure bool + TLSConfig *tls.Config // TLS config for HTTPS + Method string // http method + Body io.Reader // http request body } // NewHTTPStrategy constructs a HTTP strategy waiting on port 80 and status code 200 @@ -36,8 +40,10 @@ func NewHTTPStrategy(path string) *HTTPStrategy { Path: path, StatusCodeMatcher: defaultStatusCodeMatcher, UseTLS: false, + TLSConfig: nil, + Method: http.MethodGet, + Body: nil, } - } func defaultStatusCodeMatcher(status int) bool { @@ -63,8 +69,11 @@ func (ws *HTTPStrategy) WithStatusCodeMatcher(statusCodeMatcher func(status int) return ws } -func (ws *HTTPStrategy) WithTLS(useTLS bool) *HTTPStrategy { +func (ws *HTTPStrategy) WithTLS(useTLS bool, tlsconf ...*tls.Config) *HTTPStrategy { ws.UseTLS = useTLS + if useTLS && len(tlsconf) > 0 { + ws.TLSConfig = tlsconf[0] + } return ws } @@ -73,6 +82,16 @@ func (ws *HTTPStrategy) WithAllowInsecure(allowInsecure bool) *HTTPStrategy { return ws } +func (ws *HTTPStrategy) WithMethod(method string) *HTTPStrategy { + ws.Method = method + return ws +} + +func (ws *HTTPStrategy) WithBody(reqdata io.Reader) *HTTPStrategy { + ws.Body = reqdata + return ws +} + // ForHTTP is a convenience method similar to Wait.java // https://github.com/testcontainers/testcontainers-java/blob/1d85a3834bd937f80aad3a4cec249c027f31aeb4/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java func ForHTTP(path string) *HTTPStrategy { @@ -99,49 +118,68 @@ func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarge return errors.New("Cannot use HTTP client on non-TCP ports") } - portNumber := port.Int() - portString := strconv.Itoa(portNumber) + switch ws.Method { + case http.MethodGet, http.MethodHead, http.MethodPost: + case http.MethodPut, http.MethodPatch, http.MethodDelete: + case http.MethodConnect, http.MethodOptions, http.MethodTrace: + default: + if ws.Method != "" { + return fmt.Errorf("invalid http method %q", ws.Method) + } + ws.Method = http.MethodGet + } - address := net.JoinHostPort(ipAddress, portString) + tripper := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: ws.TLSConfig, + } var proto string if ws.UseTLS { proto = "https" + if ws.AllowInsecure { + if ws.TLSConfig == nil { + tripper.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + } else { + ws.TLSConfig.InsecureSkipVerify = true + } + } } else { proto = "http" } - url := fmt.Sprintf("%s://%s%s", proto, address, ws.Path) + client := http.Client{Transport: tripper, Timeout: time.Second} + address := net.JoinHostPort(ipAddress, strconv.Itoa(port.Int())) + endpoint := fmt.Sprintf("%s://%s%s", proto, address, ws.Path) - tripper := http.DefaultTransport - - if ws.AllowInsecure { - tripper.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - } - - client := http.Client{Timeout: ws.startupTimeout, Transport: tripper} - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return err - } - - req = req.WithContext(ctx) - -Retry: for { select { case <-ctx.Done(): - break Retry - default: + return ctx.Err() + case <-time.After(time.Second / 10): + req, err := http.NewRequestWithContext(ctx, ws.Method, endpoint, ws.Body) + if err != nil { + return err + } resp, err := client.Do(req) - if err != nil || !ws.StatusCodeMatcher(resp.StatusCode) { - time.Sleep(100 * time.Millisecond) + if err != nil { continue } - - break Retry + _ = resp.Body.Close() + if !ws.StatusCodeMatcher(resp.StatusCode) { + continue + } + return nil } } - - return nil } From 733a8bdeee9e9bf163788c0688d780538ff5cf83 Mon Sep 17 00:00:00 2001 From: islishude Date: Wed, 19 Aug 2020 22:14:02 +0800 Subject: [PATCH 2/8] add tests --- wait/http.go | 6 ++--- wait/http_test.go | 56 +++++++++++++++++++++++++++++++++++++-- wait/testdata/Dockerfile | 12 +++++++++ wait/testdata/go.mod | 3 +++ wait/testdata/main.go | 36 +++++++++++++++++++++++++ wait/testdata/root.pem | 10 +++++++ wait/testdata/tls-key.pem | 5 ++++ wait/testdata/tls.pem | 12 +++++++++ 8 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 wait/testdata/Dockerfile create mode 100644 wait/testdata/go.mod create mode 100644 wait/testdata/main.go create mode 100644 wait/testdata/root.pem create mode 100644 wait/testdata/tls-key.pem create mode 100644 wait/testdata/tls.pem diff --git a/wait/http.go b/wait/http.go index 5e7a96804b..67892cbf0e 100644 --- a/wait/http.go +++ b/wait/http.go @@ -119,9 +119,9 @@ func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarge } switch ws.Method { - case http.MethodGet, http.MethodHead, http.MethodPost: - case http.MethodPut, http.MethodPatch, http.MethodDelete: - case http.MethodConnect, http.MethodOptions, http.MethodTrace: + case http.MethodGet, http.MethodHead, http.MethodPost, + http.MethodPut, http.MethodPatch, http.MethodDelete, + http.MethodConnect, http.MethodOptions, http.MethodTrace: default: if ws.Method != "" { return fmt.Errorf("invalid http method %q", ws.Method) diff --git a/wait/http_test.go b/wait/http_test.go index 05a734eca7..43d86c46ac 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -1,7 +1,15 @@ package wait_test import ( + "bytes" "context" + "crypto/tls" + "crypto/x509" + "io/ioutil" + "net/http" + "os" + "testing" + "time" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" @@ -25,7 +33,51 @@ func ExampleHTTPStrategy() { panic(err) } - defer gogs.Terminate(ctx) - // Here you have a running container + + _ = gogs.Terminate(ctx) +} + +func TestHTTPStrategyWaitUntilReady(t *testing.T) { + workdir, err := os.Getwd() + if err != nil { + t.Error(err) + return + } + + capath := workdir + "/testdata/root.pem" + cafile, err := ioutil.ReadFile(capath) + if err != nil { + t.Errorf("can't load ca file: %v", err) + return + } + + certpool := x509.NewCertPool() + if !certpool.AppendCertsFromPEM(cafile) { + t.Errorf("the ca file isn't valid") + return + } + + dockerReq := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Context: workdir + "/testdata", + Dockerfile: workdir + "/testdata/Dockerfile", + }, + ExposedPorts: []string{"6443/tcp"}, + WaitingFor: wait.NewHTTPStrategy("/"). + WithTLS(true, &tls.Config{RootCAs: certpool, ServerName: "testcontainer.go.test"}). + WithStartupTimeout(time.Second * 10).WithPort("6443/tcp"). + WithMethod(http.MethodPost).WithBody(bytes.NewReader([]byte("ping"))), + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + container, err := testcontainers.GenericContainer(ctx, + testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: false}) + + if err != nil { + t.Error(err) + return + } + _ = container.Terminate(context.Background()) } diff --git a/wait/testdata/Dockerfile b/wait/testdata/Dockerfile new file mode 100644 index 0000000000..7bf223cdc9 --- /dev/null +++ b/wait/testdata/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.15-alpine as builder +WORKDIR /app +COPY . . +RUN mkdir -p dist +RUN go build -o ./dist/server main.go + +FROM alpine:lastest +WORKDIR /app +COPY --from=builder /app/tls.pem /app/tls-key.pem ./ +COPY --from=builder /app/dist/server . +EXPOSE 6443 +CMD ["server"] diff --git a/wait/testdata/go.mod b/wait/testdata/go.mod new file mode 100644 index 0000000000..9557d2288d --- /dev/null +++ b/wait/testdata/go.mod @@ -0,0 +1,3 @@ +module httptest + +go 1.15 diff --git a/wait/testdata/main.go b/wait/testdata/main.go new file mode 100644 index 0000000000..3b95eb07af --- /dev/null +++ b/wait/testdata/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" +) + +func main() { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + }) + + server := http.Server{Addr: "0.0.0.0:6443", Handler: mux} + go func() { + log.Println("serving...") + if err := server.ListenAndServeTLS("tls.pem", "tls-key.pem"); err != nil && err != http.ErrServerClosed { + log.Fatal(err) + } + }() + + stopsig := make(chan os.Signal, 1) + signal.Notify(stopsig, syscall.SIGINT, syscall.SIGTERM) + <-stopsig + + log.Println("stopping...") + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + _ = server.Shutdown(ctx) +} diff --git a/wait/testdata/root.pem b/wait/testdata/root.pem new file mode 100644 index 0000000000..c31286daaf --- /dev/null +++ b/wait/testdata/root.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBVTCB/aADAgECAghLWuRKnTb4BjAKBggqhkjOPQQDAjAAMB4XDTIwMDgxOTEz +MzUwOFoXDTMwMDgxNzEzNDAwOFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA +BP39G8oZK7JvdcJzSuEzoqe3KsWS7/4C7UKhdoGHkEuHED+I456v3O8x0gUTjqIv +I9FmW3cq/eMoraPzzk3u7vajYTBfMA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAU +BggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +FdfV6PSYUlHs+lSQNouRwSfR2ZgwCgYIKoZIzj0EAwIDRwAwRAIgDAFtSEaFGuvP +wJZhQv7zjIhCGzYzsZ8KSKUJ3YvdL/4CIBbgDFzEeQWFWUMFPeMaVVrmBmsflPIg +cnC4yG76skGg +-----END CERTIFICATE----- diff --git a/wait/testdata/tls-key.pem b/wait/testdata/tls-key.pem new file mode 100644 index 0000000000..00789d2371 --- /dev/null +++ b/wait/testdata/tls-key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIM8HuDwcZyVqBBy2C6db6zNb/dAJ69bq5ejAEz7qGOIQoAoGCCqGSM49 +AwEHoUQDQgAEBL2ioRmfTc70WT0vyx+amSQOGbMeoMRAfF2qaPzpzOqpKTk0aLOG +0735iy9Fz16PX4vqnLMiM/ZupugAhB//yA== +-----END EC PRIVATE KEY----- diff --git a/wait/testdata/tls.pem b/wait/testdata/tls.pem new file mode 100644 index 0000000000..46348b7900 --- /dev/null +++ b/wait/testdata/tls.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBxTCCAWugAwIBAgIUWBLNpiF1o4r+5ZXwawzPOfBM1F8wCgYIKoZIzj0EAwIw +ADAeFw0yMDA4MTkxMzM4MDBaFw0zMDA4MTcxMzM4MDBaMBkxFzAVBgNVBAMTDnRl +c3Rjb250YWluZXJzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBL2ioRmfTc70 +WT0vyx+amSQOGbMeoMRAfF2qaPzpzOqpKTk0aLOG0735iy9Fz16PX4vqnLMiM/Zu +pugAhB//yKOBqTCBpjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH +AwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUTMdz5PIZ+Gix4jYUzRIHfByrW+Yw +HwYDVR0jBBgwFoAUFdfV6PSYUlHs+lSQNouRwSfR2ZgwMQYDVR0RBCowKIIVdGVz +dGNvbnRhaW5lci5nby50ZXN0gglsb2NhbGhvc3SHBH8AAAEwCgYIKoZIzj0EAwID +SAAwRQIhAJznPNumi2Plf0GsP9DpC+8WukT/jUhnhcDWCfZ6Ini2AiBLhnhFebZX +XWfSsdSNxIo20OWvy6z3wqdybZtRUfdU+g== +-----END CERTIFICATE----- From c563b7e95e68bbc0e9e48fc3f6b0deb1dd23c25a Mon Sep 17 00:00:00 2001 From: islishude Date: Wed, 19 Aug 2020 22:31:10 +0800 Subject: [PATCH 3/8] wip --- wait/http_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wait/http_test.go b/wait/http_test.go index 43d86c46ac..248c7df4fa 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -61,7 +61,7 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { dockerReq := testcontainers.ContainerRequest{ FromDockerfile: testcontainers.FromDockerfile{ Context: workdir + "/testdata", - Dockerfile: workdir + "/testdata/Dockerfile", + Dockerfile: "Dockerfile", }, ExposedPorts: []string{"6443/tcp"}, WaitingFor: wait.NewHTTPStrategy("/"). From befd48ee0b6c6e718aa1b7cb5b49c69ef0f98b8e Mon Sep 17 00:00:00 2001 From: islishude Date: Wed, 19 Aug 2020 22:39:33 +0800 Subject: [PATCH 4/8] wip --- wait/http_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wait/http_test.go b/wait/http_test.go index 248c7df4fa..83d9bbd827 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -73,7 +73,7 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() container, err := testcontainers.GenericContainer(ctx, - testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: false}) + testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: true}) if err != nil { t.Error(err) From 8443e25f04de5b9095b0ea97ef1c0b6345b636cb Mon Sep 17 00:00:00 2001 From: islishude Date: Wed, 19 Aug 2020 23:55:49 +0800 Subject: [PATCH 5/8] wip --- wait/http_test.go | 49 +++++++++++++++++++++++++++++++++------- wait/testdata/Dockerfile | 4 ++-- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/wait/http_test.go b/wait/http_test.go index 83d9bbd827..d4cb9e46f4 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -5,6 +5,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "fmt" "io/ioutil" "net/http" "os" @@ -58,26 +59,58 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { return } + tlsconfig := &tls.Config{RootCAs: certpool, ServerName: "testcontainer.go.test"} dockerReq := testcontainers.ContainerRequest{ FromDockerfile: testcontainers.FromDockerfile{ - Context: workdir + "/testdata", - Dockerfile: "Dockerfile", + Context: workdir + "/testdata", }, ExposedPorts: []string{"6443/tcp"}, - WaitingFor: wait.NewHTTPStrategy("/"). - WithTLS(true, &tls.Config{RootCAs: certpool, ServerName: "testcontainer.go.test"}). + WaitingFor: wait.NewHTTPStrategy("/").WithTLS(true, tlsconfig). WithStartupTimeout(time.Second * 10).WithPort("6443/tcp"). WithMethod(http.MethodPost).WithBody(bytes.NewReader([]byte("ping"))), } - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - container, err := testcontainers.GenericContainer(ctx, + t.Log("creating container") + container, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: true}) + if err != nil { + t.Error(err) + return + } + defer container.Terminate(context.Background()) // nolint: errcheck + t.Log("requesting") + host, err := container.Host(context.Background()) if err != nil { t.Error(err) return } - _ = container.Terminate(context.Background()) + port, err := container.MappedPort(context.Background(), "6443/tcp") + if err != nil { + t.Error(err) + return + } + client := http.Client{Transport: &http.Transport{TLSClientConfig: tlsconfig}} + resp, err := client.Get(fmt.Sprintf("https://%s:%s/ping", host, port.Port())) + if err != nil { + t.Error(err) + return + } + defer resp.Body.Close() + + t.Log("verify http status code") + if resp.StatusCode != http.StatusOK { + t.Errorf("status code isn't ok: %s", resp.Status) + return + } + + t.Log("verify response data") + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Error(err) + return + } + if string(data) != "pong" { + t.Errorf("should returns 'pong'") + } } diff --git a/wait/testdata/Dockerfile b/wait/testdata/Dockerfile index 7bf223cdc9..d89165a2e0 100644 --- a/wait/testdata/Dockerfile +++ b/wait/testdata/Dockerfile @@ -4,9 +4,9 @@ COPY . . RUN mkdir -p dist RUN go build -o ./dist/server main.go -FROM alpine:lastest +FROM alpine WORKDIR /app COPY --from=builder /app/tls.pem /app/tls-key.pem ./ COPY --from=builder /app/dist/server . EXPOSE 6443 -CMD ["server"] +CMD ["/app/server"] From bc8571e35f0824b6e2f81beae6ad95a9e37b5b49 Mon Sep 17 00:00:00 2001 From: islishude Date: Thu, 20 Aug 2020 08:15:36 +0800 Subject: [PATCH 6/8] wip --- wait/http_test.go | 16 +--------------- wait/testdata/main.go | 8 ++++++-- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/wait/http_test.go b/wait/http_test.go index d4cb9e46f4..c44e19c72b 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -34,9 +34,9 @@ func ExampleHTTPStrategy() { panic(err) } + defer gogs.Terminate(ctx) // Here you have a running container - _ = gogs.Terminate(ctx) } func TestHTTPStrategyWaitUntilReady(t *testing.T) { @@ -70,7 +70,6 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { WithMethod(http.MethodPost).WithBody(bytes.NewReader([]byte("ping"))), } - t.Log("creating container") container, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: true}) if err != nil { @@ -79,7 +78,6 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { } defer container.Terminate(context.Background()) // nolint: errcheck - t.Log("requesting") host, err := container.Host(context.Background()) if err != nil { t.Error(err) @@ -97,20 +95,8 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { return } defer resp.Body.Close() - - t.Log("verify http status code") if resp.StatusCode != http.StatusOK { t.Errorf("status code isn't ok: %s", resp.Status) return } - - t.Log("verify response data") - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Error(err) - return - } - if string(data) != "pong" { - t.Errorf("should returns 'pong'") - } } diff --git a/wait/testdata/main.go b/wait/testdata/main.go index 3b95eb07af..ebccdc908d 100644 --- a/wait/testdata/main.go +++ b/wait/testdata/main.go @@ -14,10 +14,14 @@ func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("ok")) }) - server := http.Server{Addr: "0.0.0.0:6443", Handler: mux} + mux.HandleFunc("/ping", func(w http.ResponseWriter, req *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("pong")) + }) + + server := http.Server{Addr: ":6443", Handler: mux} go func() { log.Println("serving...") if err := server.ListenAndServeTLS("tls.pem", "tls-key.pem"); err != nil && err != http.ErrServerClosed { From e56e93fac0e59c751050e01bb3152360e80bf7dd Mon Sep 17 00:00:00 2001 From: islishude Date: Thu, 20 Aug 2020 08:23:56 +0800 Subject: [PATCH 7/8] test pass --- wait/http_test.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/wait/http_test.go b/wait/http_test.go index c44e19c72b..a7d899be67 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -7,8 +7,10 @@ import ( "crypto/x509" "fmt" "io/ioutil" + "net" "net/http" "os" + "strconv" "testing" "time" @@ -78,7 +80,7 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { } defer container.Terminate(context.Background()) // nolint: errcheck - host, err := container.Host(context.Background()) + ipAddress, err := container.Host(context.Background()) if err != nil { t.Error(err) return @@ -88,8 +90,23 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { t.Error(err) return } - client := http.Client{Transport: &http.Transport{TLSClientConfig: tlsconfig}} - resp, err := client.Get(fmt.Sprintf("https://%s:%s/ping", host, port.Port())) + client := http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsconfig, + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, + } + resp, err := client.Get(fmt.Sprintf("https://%s/ping", net.JoinHostPort(ipAddress, strconv.Itoa(port.Int())))) if err != nil { t.Error(err) return From 5c27338accd4cd1270d53fc6c741cc46d9e9cbd3 Mon Sep 17 00:00:00 2001 From: islishude Date: Thu, 20 Aug 2020 08:56:56 +0800 Subject: [PATCH 8/8] tidy codes --- wait/http.go | 16 ++++++++++++++-- wait/http_test.go | 14 +++++++++----- wait/testdata/main.go | 11 +++++++++-- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/wait/http.go b/wait/http.go index 67892cbf0e..ff1031b041 100644 --- a/wait/http.go +++ b/wait/http.go @@ -25,6 +25,7 @@ type HTTPStrategy struct { Port nat.Port Path string StatusCodeMatcher func(status int) bool + ResponseMatcher func(body io.Reader) bool UseTLS bool AllowInsecure bool TLSConfig *tls.Config // TLS config for HTTPS @@ -39,6 +40,7 @@ func NewHTTPStrategy(path string) *HTTPStrategy { Port: "80/tcp", Path: path, StatusCodeMatcher: defaultStatusCodeMatcher, + ResponseMatcher: func(body io.Reader) bool { return true }, UseTLS: false, TLSConfig: nil, Method: http.MethodGet, @@ -69,6 +71,11 @@ func (ws *HTTPStrategy) WithStatusCodeMatcher(statusCodeMatcher func(status int) return ws } +func (ws *HTTPStrategy) WithResponseMatcher(matcher func(body io.Reader) bool) *HTTPStrategy { + ws.ResponseMatcher = matcher + return ws +} + func (ws *HTTPStrategy) WithTLS(useTLS bool, tlsconf ...*tls.Config) *HTTPStrategy { ws.UseTLS = useTLS if useTLS && len(tlsconf) > 0 { @@ -175,8 +182,13 @@ func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarge if err != nil { continue } - _ = resp.Body.Close() - if !ws.StatusCodeMatcher(resp.StatusCode) { + if ws.StatusCodeMatcher != nil && !ws.StatusCodeMatcher(resp.StatusCode) { + continue + } + if ws.ResponseMatcher != nil && !ws.ResponseMatcher(resp.Body) { + continue + } + if err := resp.Body.Close(); err != nil { continue } return nil diff --git a/wait/http_test.go b/wait/http_test.go index a7d899be67..a3a34431e1 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -6,11 +6,11 @@ import ( "crypto/tls" "crypto/x509" "fmt" + "io" "io/ioutil" "net" "net/http" "os" - "strconv" "testing" "time" @@ -36,7 +36,7 @@ func ExampleHTTPStrategy() { panic(err) } - defer gogs.Terminate(ctx) + defer gogs.Terminate(ctx) // nolint: errcheck // Here you have a running container } @@ -67,8 +67,12 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { Context: workdir + "/testdata", }, ExposedPorts: []string{"6443/tcp"}, - WaitingFor: wait.NewHTTPStrategy("/").WithTLS(true, tlsconfig). + WaitingFor: wait.NewHTTPStrategy("/ping").WithTLS(true, tlsconfig). WithStartupTimeout(time.Second * 10).WithPort("6443/tcp"). + WithResponseMatcher(func(body io.Reader) bool { + data, _ := ioutil.ReadAll(body) + return bytes.Equal(data, []byte("pong")) + }). WithMethod(http.MethodPost).WithBody(bytes.NewReader([]byte("ping"))), } @@ -80,7 +84,7 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { } defer container.Terminate(context.Background()) // nolint: errcheck - ipAddress, err := container.Host(context.Background()) + host, err := container.Host(context.Background()) if err != nil { t.Error(err) return @@ -106,7 +110,7 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { ExpectContinueTimeout: 1 * time.Second, }, } - resp, err := client.Get(fmt.Sprintf("https://%s/ping", net.JoinHostPort(ipAddress, strconv.Itoa(port.Int())))) + resp, err := client.Get(fmt.Sprintf("https://%s:%s", host, port.Port())) if err != nil { t.Error(err) return diff --git a/wait/testdata/main.go b/wait/testdata/main.go index ebccdc908d..a8bdfc2ee4 100644 --- a/wait/testdata/main.go +++ b/wait/testdata/main.go @@ -1,7 +1,9 @@ package main import ( + "bytes" "context" + "io/ioutil" "log" "net/http" "os" @@ -17,8 +19,13 @@ func main() { }) mux.HandleFunc("/ping", func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("pong")) + data, _ := ioutil.ReadAll(req.Body) + if bytes.Equal(data, []byte("ping")) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("pong")) + } else { + w.WriteHeader(http.StatusBadRequest) + } }) server := http.Server{Addr: ":6443", Handler: mux}