Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add HTTPStrategy WithForcedIPv4LocalHost To Fix Docker Port Map #1775

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions wait/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"time"

"github.com/docker/go-connections/nat"
Expand All @@ -27,17 +28,18 @@ type HTTPStrategy struct {
timeout *time.Duration

// additional properties
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
Method string // http method
Body io.Reader // http request body
PollInterval time.Duration
UserInfo *url.Userinfo
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
Method string // http method
Body io.Reader // http request body
PollInterval time.Duration
UserInfo *url.Userinfo
ForceIPv4LocalHost bool
}

// NewHTTPStrategy constructs a HTTP strategy waiting on port 80 and status code 200
Expand Down Expand Up @@ -119,6 +121,13 @@ func (ws *HTTPStrategy) WithPollInterval(pollInterval time.Duration) *HTTPStrate
return ws
}

// WithForcedIPv4LocalHost forces usage of localhost to be ipv4 127.0.0.1
// to avoid ipv6 docker bugs https://github.com/moby/moby/issues/42442 https://github.com/moby/moby/issues/42375
func (ws *HTTPStrategy) WithForcedIPv4LocalHost() *HTTPStrategy {
ws.ForceIPv4LocalHost = true
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 {
Expand All @@ -143,6 +152,10 @@ func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarge
if err != nil {
return err
}
// to avoid ipv6 docker bugs https://github.com/moby/moby/issues/42442 https://github.com/moby/moby/issues/42375
if ws.ForceIPv4LocalHost {
ipAddress = strings.Replace(ipAddress, "localhost", "127.0.0.1", 1)
}

var mappedPort nat.Port
if ws.Port == "" {
Expand Down
45 changes: 39 additions & 6 deletions wait/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func ExampleHTTPStrategy() {
WaitingFor: wait.ForHTTP("/").WithStartupTimeout(10 * time.Second),
}

gogs, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
Expand All @@ -42,12 +42,12 @@ func ExampleHTTPStrategy() {
// }

defer func() {
if err := gogs.Terminate(ctx); err != nil {
if err := c.Terminate(ctx); err != nil {
log.Fatalf("failed to terminate container: %s", err)
}
}()

state, err := gogs.State(ctx)
state, err := c.State(ctx)
if err != nil {
panic(err)
}
Expand All @@ -67,7 +67,7 @@ func ExampleHTTPStrategy_WithPort() {
WaitingFor: wait.ForHTTP("/").WithPort("80/tcp"),
}

gogs, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
Expand All @@ -77,12 +77,45 @@ func ExampleHTTPStrategy_WithPort() {
// }

defer func() {
if err := gogs.Terminate(ctx); err != nil {
if err := c.Terminate(ctx); err != nil {
log.Fatalf("failed to terminate container: %s", err)
}
}()

state, err := gogs.State(ctx)
state, err := c.State(ctx)
if err != nil {
panic(err)
}

fmt.Println(state.Running)

// Output:
// true
}

func ExampleHTTPStrategy_WithForcedIPv4LocalHost() {
ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "nginx:latest",
ExposedPorts: []string{"8080/tcp", "80/tcp"},
WaitingFor: wait.ForHTTP("/").WithForcedIPv4LocalHost(),
}

c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
if err != nil {
panic(err)
}

defer func() {
if err := c.Terminate(ctx); err != nil {
log.Fatalf("failed to terminate container: %s", err)
}
}()
mdelapenya marked this conversation as resolved.
Show resolved Hide resolved

state, err := c.State(ctx)
if err != nil {
panic(err)
}
Expand Down
Loading