From 0a4d6b42ae8b224a7df6f228c38545eca0655805 Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Sat, 18 Jun 2022 01:15:24 +0500 Subject: [PATCH 1/8] feat: wait any port if target port is empty --- docker.go | 21 ++++++++++++++++----- docker_test.go | 30 ++++++++++++++++++++++++++++++ wait/host_port.go | 21 ++++++++++++++++++--- wait/wait.go | 1 + 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/docker.go b/docker.go index 466b538d30..645a172855 100644 --- a/docker.go +++ b/docker.go @@ -759,11 +759,6 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque } } - exposedPortSet, exposedPortMap, err := nat.ParsePortSpecs(req.ExposedPorts) - if err != nil { - return nil, err - } - env := []string{} for envKey, envVar := range req.Env { env = append(env, envKey+"="+envVar) @@ -848,6 +843,22 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque } } + exposedPorts := req.ExposedPorts + if len(exposedPorts) == 0 { + image, _, err := p.client.ImageInspectWithRaw(ctx, tag) + if err != nil { + return nil, err + } + for p, _ := range image.ContainerConfig.ExposedPorts { + exposedPorts = append(exposedPorts, string(p)) + } + } + + exposedPortSet, exposedPortMap, err := nat.ParsePortSpecs(exposedPorts) + if err != nil { + return nil, err + } + dockerInput := &container.Config{ Entrypoint: req.Entrypoint, Image: tag, diff --git a/docker_test.go b/docker_test.go index 08dc6f3787..c479bee1a0 100644 --- a/docker_test.go +++ b/docker_test.go @@ -146,6 +146,36 @@ func TestContainerWithHostNetworkOptions(t *testing.T) { } } +func TestContainerWithHostNetworkOptions_UseExposePortsFromImageConfigs(t *testing.T) { + ctx := context.Background() + gcr := GenericContainerRequest{ + ContainerRequest: ContainerRequest{ + Image: "nginx", + Privileged: true, + SkipReaper: true, + WaitingFor: wait.ForListeningPort(""), + }, + Started: true, + } + + nginxC, err := GenericContainer(ctx, gcr) + if err != nil { + t.Fatal(err) + } + + defer nginxC.Terminate(ctx) + + endpoint, err := nginxC.Endpoint(ctx, "http") + if err != nil { + t.Errorf("Expected server endpoint. Got '%v'.", err) + } + + _, err = http.Get(endpoint) + if err != nil { + t.Errorf("Expected OK response. Got '%d'.", err) + } +} + func TestContainerWithNetworkModeAndNetworkTogether(t *testing.T) { ctx := context.Background() gcr := GenericContainerRequest{ diff --git a/wait/host_port.go b/wait/host_port.go index 4c3a25fa41..0329356a25 100644 --- a/wait/host_port.go +++ b/wait/host_port.go @@ -57,8 +57,23 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT var waitInterval = 100 * time.Millisecond + internalPort := hp.Port + if internalPort == "" { + var ports nat.PortMap + ports, err = target.Ports(ctx) + if err != nil { + return + } + if len(ports) > 0 { + for p := range ports { + internalPort = p + break + } + } + } + var port nat.Port - port, err = target.MappedPort(ctx, hp.Port) + port, err = target.MappedPort(ctx, internalPort) var i = 0 for port == "" { @@ -68,7 +83,7 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT case <-ctx.Done(): return fmt.Errorf("%s:%w", ctx.Err(), err) case <-time.After(waitInterval): - port, err = target.MappedPort(ctx, hp.Port) + port, err = target.MappedPort(ctx, internalPort) if err != nil { fmt.Printf("(%d) [%s] %s\n", i, port, err) } @@ -101,7 +116,7 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT } //internal check - command := buildInternalCheckCommand(hp.Port.Int()) + command := buildInternalCheckCommand(internalPort.Int()) for { if ctx.Err() != nil { return ctx.Err() diff --git a/wait/wait.go b/wait/wait.go index 914483b9ca..fedee44d5f 100644 --- a/wait/wait.go +++ b/wait/wait.go @@ -15,6 +15,7 @@ type Strategy interface { type StrategyTarget interface { Host(context.Context) (string, error) + Ports(ctx context.Context) (nat.PortMap, error) MappedPort(context.Context, nat.Port) (nat.Port, error) Logs(context.Context) (io.ReadCloser, error) Exec(ctx context.Context, cmd []string) (int, io.Reader, error) From 835c56dba4879be4b01786081c66e48c7f5e7635 Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Mon, 11 Jul 2022 20:41:04 +0500 Subject: [PATCH 2/8] fix: return error if exposed port not found --- wait/host_port.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wait/host_port.go b/wait/host_port.go index 0329356a25..0d53784738 100644 --- a/wait/host_port.go +++ b/wait/host_port.go @@ -72,6 +72,11 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT } } + if internalPort == "" { + err = fmt.Errorf("no port to waiting") + return + } + var port nat.Port port, err = target.MappedPort(ctx, internalPort) var i = 0 From b91ae6c40d3d8a8fe3001b8b2ec5ef931a6f5e36 Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Mon, 11 Jul 2022 20:44:40 +0500 Subject: [PATCH 3/8] fix: implement strategy interface --- wait/exit_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wait/exit_test.go b/wait/exit_test.go index 63563d640d..d7a6b476af 100644 --- a/wait/exit_test.go +++ b/wait/exit_test.go @@ -19,6 +19,10 @@ func (st exitStrategyTarget) Host(ctx context.Context) (string, error) { return "", nil } +func (st exitStrategyTarget) Ports(ctx context.Context) (nat.PortMap, error) { + return nil, nil +} + func (st exitStrategyTarget) MappedPort(ctx context.Context, n nat.Port) (nat.Port, error) { return n, nil } From a73abdda172081132860668ff00f7b56da382998 Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Mon, 11 Jul 2022 20:48:44 +0500 Subject: [PATCH 4/8] fix: implement strategy interface --- wait/exec_test.go | 4 ++++ wait/log_test.go | 3 +++ 2 files changed, 7 insertions(+) diff --git a/wait/exec_test.go b/wait/exec_test.go index c5a932f40f..f97d799295 100644 --- a/wait/exec_test.go +++ b/wait/exec_test.go @@ -44,6 +44,10 @@ func (st mockExecTarget) Host(_ context.Context) (string, error) { return "", errors.New("not implemented") } +func (st mockExecTarget) Ports(ctx context.Context) (nat.PortMap, error) { + return nil, errors.New("not implemented") +} + func (st mockExecTarget) MappedPort(_ context.Context, n nat.Port) (nat.Port, error) { return n, errors.New("not implemented") } diff --git a/wait/log_test.go b/wait/log_test.go index add532eaca..e570b30681 100644 --- a/wait/log_test.go +++ b/wait/log_test.go @@ -20,6 +20,9 @@ func (st noopStrategyTarget) Host(ctx context.Context) (string, error) { return "", nil } +func (st noopStrategyTarget) Ports(ctx context.Context) (nat.PortMap, error) { + return nil, nil +} func (st noopStrategyTarget) MappedPort(ctx context.Context, n nat.Port) (nat.Port, error) { return n, nil } From 04fab289b8478a9eb1b957a09035f423f2b2d76e Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Tue, 12 Jul 2022 13:13:40 +0500 Subject: [PATCH 5/8] Update wait/host_port.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- wait/host_port.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wait/host_port.go b/wait/host_port.go index 0d53784738..6264b6d752 100644 --- a/wait/host_port.go +++ b/wait/host_port.go @@ -73,7 +73,7 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT } if internalPort == "" { - err = fmt.Errorf("no port to waiting") + err = fmt.Errorf("no port to wait for") return } From c2e4956e71b68e55c139de52a676ba8a29fbfdc3 Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Tue, 12 Jul 2022 13:13:50 +0500 Subject: [PATCH 6/8] Update wait/log_test.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- wait/log_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/wait/log_test.go b/wait/log_test.go index e570b30681..fdd9db9fc3 100644 --- a/wait/log_test.go +++ b/wait/log_test.go @@ -23,6 +23,7 @@ func (st noopStrategyTarget) Host(ctx context.Context) (string, error) { func (st noopStrategyTarget) Ports(ctx context.Context) (nat.PortMap, error) { return nil, nil } + func (st noopStrategyTarget) MappedPort(ctx context.Context, n nat.Port) (nat.Port, error) { return n, nil } From 415440af170fa8bd3803bec3c9f865db7257ddcb Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Tue, 12 Jul 2022 14:25:17 +0500 Subject: [PATCH 7/8] feat: add wait.ForExposedPort --- docker_test.go | 2 +- wait/host_port.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docker_test.go b/docker_test.go index c479bee1a0..df10d0227c 100644 --- a/docker_test.go +++ b/docker_test.go @@ -153,7 +153,7 @@ func TestContainerWithHostNetworkOptions_UseExposePortsFromImageConfigs(t *testi Image: "nginx", Privileged: true, SkipReaper: true, - WaitingFor: wait.ForListeningPort(""), + WaitingFor: wait.ForExposedPort(), }, Started: true, } diff --git a/wait/host_port.go b/wait/host_port.go index 6264b6d752..019e659bf1 100644 --- a/wait/host_port.go +++ b/wait/host_port.go @@ -16,6 +16,8 @@ import ( var _ Strategy = (*HostPortStrategy)(nil) type HostPortStrategy struct { + // Port is a string containing port number and protocol in the format "80/tcp" + // which Port nat.Port // all WaitStrategies should have a startupTimeout to avoid waiting infinitely startupTimeout time.Duration @@ -39,6 +41,12 @@ func ForListeningPort(port nat.Port) *HostPortStrategy { return NewHostPortStrategy(port) } +// ForExposedPort constructs an exposed port strategy. Alias for `NewHostPortStrategy("")`. +// This strategy waits port exposed in Docker container. +func ForExposedPort() *HostPortStrategy { + return NewHostPortStrategy("") +} + func (hp *HostPortStrategy) WithStartupTimeout(startupTimeout time.Duration) *HostPortStrategy { hp.startupTimeout = startupTimeout return hp From 37e1e954cf736898a64b7ab94883bd053ec837f8 Mon Sep 17 00:00:00 2001 From: Denis Gukov Date: Tue, 12 Jul 2022 18:56:38 +0500 Subject: [PATCH 8/8] Update wait/host_port.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel de la Peña --- wait/host_port.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wait/host_port.go b/wait/host_port.go index 019e659bf1..98be362901 100644 --- a/wait/host_port.go +++ b/wait/host_port.go @@ -42,7 +42,7 @@ func ForListeningPort(port nat.Port) *HostPortStrategy { } // ForExposedPort constructs an exposed port strategy. Alias for `NewHostPortStrategy("")`. -// This strategy waits port exposed in Docker container. +// This strategy waits for the first port exposed in the Docker container. func ForExposedPort() *HostPortStrategy { return NewHostPortStrategy("") }