Skip to content

Commit

Permalink
Merge branch 'main' into v1
Browse files Browse the repository at this point in the history
* main:
  fix: missing image build errors (#2651)
  test: fix image label test (#2689)
  chore(deps): bump github.com/docker/docker from 27.0.3+incompatible to 27.1.0+incompatible (#2682)
  chore: print Docker Info labels in banner (#2681)
  fix: incorrect parsing of exposedPorts in readiness check (#2658)
  • Loading branch information
mdelapenya committed Aug 7, 2024
2 parents e9da485 + 82838e2 commit 36fff00
Show file tree
Hide file tree
Showing 103 changed files with 307 additions and 184 deletions.
2 changes: 1 addition & 1 deletion container.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func Run(ctx context.Context, req Request) (*DockerContainer, error) {
}
if err != nil {
// At this point `c` might not be nil. Give the caller an opportunity to call Destroy on the container.
return c, fmt.Errorf("%w: failed to create container", err)
return c, fmt.Errorf("create container: %w", err)
}

if req.Started && !c.IsRunning() {
Expand Down
23 changes: 11 additions & 12 deletions container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func TestBuildImageWithContexts(t *testing.T) {
ContextArchive func() (io.Reader, error)
ExpectedEchoOutput string
Dockerfile string
ExpectedError error
ExpectedError string
}

testCases := []TestCase{
Expand Down Expand Up @@ -252,7 +252,7 @@ func TestBuildImageWithContexts(t *testing.T) {
ExpectedEchoOutput: "hi this is from the say_hi.sh file!",
},
{
Name: "test buildling from a context on the filesystem",
Name: "test building from a context on the filesystem",
ContextPath: "./testdata",
Dockerfile: "echo.Dockerfile",
ExpectedEchoOutput: "this is from the echo test Dockerfile",
Expand All @@ -266,7 +266,7 @@ func TestBuildImageWithContexts(t *testing.T) {
ContextArchive: func() (io.Reader, error) {
return nil, nil
},
ExpectedError: errors.New("you must specify either a build context or an image: failed to create container"),
ExpectedError: "create container: you must specify either a build context or an image",
},
}

Expand All @@ -290,16 +290,15 @@ func TestBuildImageWithContexts(t *testing.T) {
}

c, err := testcontainers.Run(ctx, req)
switch {
case testCase.ExpectedError != nil && err != nil:
if testCase.ExpectedError.Error() != err.Error() {
t.Fatalf("unexpected error: %s, was expecting %s", err.Error(), testCase.ExpectedError.Error())
}
case err != nil:
t.Fatal(err)
default:
testcontainers.TerminateContainerOnEnd(t, ctx, c)

defer testcontainers.TerminateContainerOnEnd(t, ctx, c)

if testCase.ExpectedError != "" {
require.EqualError(t, err, testCase.ExpectedError)
return
}

require.NoError(t, err)
})
}
}
Expand Down
15 changes: 10 additions & 5 deletions docker_mounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,21 @@ func TestMountsReceiveRyukLabels(t *testing.T) {
}

ctx := context.Background()
client, err := core.NewClient(ctx)
require.NoError(t, err)
defer client.Close()

// Ensure the volume is removed before creating the container
// otherwise the volume will be reused and the labels won't be set.
err = client.VolumeRemove(ctx, "app-data", true)
require.NoError(t, err)

c, err := testcontainers.Run(ctx, req)
require.NoError(t, err)
testcontainers.TerminateContainerOnEnd(t, ctx, c)

// Check if volume is created with the expected labels
client, err := core.NewClient(ctx)
require.NoError(t, err)
defer client.Close()

volume, err := client.VolumeInspect(ctx, "app-data")
require.NoError(t, err)
assert.Equal(t, core.DefaultLabels(core.SessionID()), volume.Labels)
require.Equal(t, core.DefaultLabels(core.SessionID()), volume.Labels)
}
2 changes: 1 addition & 1 deletion examples/nginx/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v27.0.3+incompatible // indirect
github.com/docker/docker v27.1.0+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
Expand Down
4 changes: 2 additions & 2 deletions examples/nginx/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down
2 changes: 1 addition & 1 deletion examples/toxiproxy/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v27.0.3+incompatible // indirect
github.com/docker/docker v27.1.0+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
Expand Down
4 changes: 2 additions & 2 deletions examples/toxiproxy/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down
20 changes: 20 additions & 0 deletions from_dockerfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"path/filepath"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -138,6 +139,25 @@ func TestBuildImageFromDockerfile_NoRepo(t *testing.T) {
})
}

func TestBuildImageFromDockerfile_BuildError(t *testing.T) {
ctx := context.Background()
dockerClient, err := core.NewClient(ctx)
require.NoError(t, err)

defer dockerClient.Close()

req := Request{
FromDockerfile: FromDockerfile{
Dockerfile: "error.Dockerfile",
Context: filepath.Join(".", "testdata"),
},
Started: true,
}
_, err = Run(ctx, req)

require.EqualError(t, err, `create container: build image: The command '/bin/sh -c exit 1' returned a non-zero code: 1`)
}

func TestBuildImageFromDockerfile_NoTag(t *testing.T) {
ctx := context.Background()

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/cenkalti/backoff/v4 v4.2.1
github.com/containerd/platforms v0.2.1
github.com/cpuguy83/dockercfg v0.3.1
github.com/docker/docker v27.0.3+incompatible
github.com/docker/docker v27.1.0+incompatible
github.com/docker/go-connections v0.5.0
github.com/google/uuid v1.6.0
github.com/magiconair/properties v1.8.7
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down
5 changes: 3 additions & 2 deletions image/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package image

import (
"context"
"fmt"
"io"
"os"
"time"
Expand Down Expand Up @@ -42,14 +43,14 @@ func Build(ctx context.Context, img BuildInfo) (string, error) {
var err error
buildOptions, err = img.BuildOptions()
if err != nil {
return types.ImageBuildResponse{}, backoff.Permanent(err)
return types.ImageBuildResponse{}, backoff.Permanent(fmt.Errorf("build options: %w", err))
}
defer tryClose(buildOptions.Context) // release resources in any case

resp, err := cli.ImageBuild(ctx, buildOptions.Context, buildOptions)
if err != nil {
if core.IsPermanentClientError(err) {
return types.ImageBuildResponse{}, backoff.Permanent(err)
return types.ImageBuildResponse{}, backoff.Permanent(fmt.Errorf("build image: %w", err))
}

img.Printf("Failed to build image: %s, will retry", err)
Expand Down
15 changes: 13 additions & 2 deletions internal/core/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,28 @@ func (c *DockerClient) Info(ctx context.Context) (system.Info, error) {
Server Version: %v
API Version: %v
Operating System: %v
Total Memory: %v MB
Total Memory: %v MB%s
Testcontainers for Go Version: v%s
Resolved Docker Host: %s
Resolved Docker Socket Path: %s
Test SessionID: %s
Test ProcessID: %s
`

infoLabels := ""
if len(dockerInfo.Labels) > 0 {
infoLabels = `
Labels:`
for _, lb := range dockerInfo.Labels {
infoLabels += "\n " + lb
}
}

c.logger.Printf(infoMessage, packagePath,
dockerInfo.ServerVersion, c.APIClient.ClientVersion(),
dockerInfo.ServerVersion,
c.ClientVersion(),
dockerInfo.OperatingSystem, dockerInfo.MemTotal/1024/1024,
infoLabels,
internal.Version,
ExtractDockerHost(ctx),
ExtractDockerSocket(ctx),
Expand Down
49 changes: 29 additions & 20 deletions lifecycle_ready.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,7 @@ var defaultReadinessHook = func() LifecycleHooks {
return err
}

exposedAndMappedPorts := jsonRaw.NetworkSettings.Ports

for _, exposedPort := range dockerContainer.exposedPorts {
portMap := nat.Port(exposedPort)
// having entries in exposedAndMappedPorts, where the key is the exposed port,
// and the value is the mapped port, means that the port has been already mapped.
if _, ok := exposedAndMappedPorts[portMap]; !ok {
// check if the port is mapped with the protocol (default is TCP)
if !strings.Contains(exposedPort, "/") {
portMap = nat.Port(fmt.Sprintf("%s/tcp", exposedPort))
if _, ok := exposedAndMappedPorts[portMap]; !ok {
return fmt.Errorf("port %s is not mapped yet", exposedPort)
}
} else {
return fmt.Errorf("port %s is not mapped yet", exposedPort)
}
}
}

return nil
return checkPortsMapped(jsonRaw.NetworkSettings.Ports, dockerContainer.exposedPorts)
},
b,
func(err error, duration time.Duration) {
Expand Down Expand Up @@ -85,3 +66,31 @@ var defaultReadinessHook = func() LifecycleHooks {
},
}
}

func checkPortsMapped(exposedAndMappedPorts nat.PortMap, exposedPorts []string) error {
portMap, _, err := nat.ParsePortSpecs(exposedPorts)
if err != nil {
return fmt.Errorf("parse exposed ports: %w", err)
}

for exposedPort := range portMap {
// having entries in exposedAndMappedPorts, where the key is the exposed port,
// and the value is the mapped port, means that the port has been already mapped.
if _, ok := exposedAndMappedPorts[exposedPort]; ok {
continue
}

// check if the port is mapped with the protocol (default is TCP)
if strings.Contains(string(exposedPort), "/") {
return fmt.Errorf("port %s is not mapped yet", exposedPort)
}

// Port didn't have a type, default to tcp and retry.
exposedPort += "/tcp"
if _, ok := exposedAndMappedPorts[exposedPort]; !ok {
return fmt.Errorf("port %s is not mapped yet", exposedPort)
}
}

return nil
}
75 changes: 75 additions & 0 deletions lifecycle_ready_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package testcontainers

import (
"testing"

"github.com/docker/go-connections/nat"
"github.com/stretchr/testify/require"
)

func TestPortMappingCheck(t *testing.T) {
makePortMap := func(ports ...string) nat.PortMap {
out := make(nat.PortMap)
for _, port := range ports {
// We don't care about the actual binding in this test
out[nat.Port(port)] = nil
}
return out
}

tests := map[string]struct {
exposedAndMappedPorts nat.PortMap
exposedPorts []string
expectError bool
}{
"no-protocol": {
exposedAndMappedPorts: makePortMap("1024/tcp"),
exposedPorts: []string{"1024"},
},
"protocol": {
exposedAndMappedPorts: makePortMap("1024/tcp"),
exposedPorts: []string{"1024/tcp"},
},
"protocol-target-port": {
exposedAndMappedPorts: makePortMap("1024/tcp"),
exposedPorts: []string{"1024:1024/tcp"},
},
"target-port": {
exposedAndMappedPorts: makePortMap("1024/tcp"),
exposedPorts: []string{"1024:1024"},
},
"multiple-ports": {
exposedAndMappedPorts: makePortMap("1024/tcp", "1025/tcp", "1026/tcp"),
exposedPorts: []string{"1024", "25:1025/tcp", "1026:1026"},
},
"only-ipv4": {
exposedAndMappedPorts: makePortMap("1024/tcp"),
exposedPorts: []string{"0.0.0.0::1024/tcp"},
},
"no-mapped-ports": {
exposedAndMappedPorts: makePortMap(),
exposedPorts: []string{"1024"},
expectError: true,
},
"wrong-mapped-port": {
exposedAndMappedPorts: makePortMap("1023/tcp"),
exposedPorts: []string{"1024"},
expectError: true,
},
"subset-mapped-ports": {
exposedAndMappedPorts: makePortMap("1024/tcp", "1025/tcp"),
exposedPorts: []string{"1024", "1025", "1026"},
expectError: true,
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
err := checkPortsMapped(tt.exposedAndMappedPorts, tt.exposedPorts)
if tt.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
}
2 changes: 1 addition & 1 deletion modules/artemis/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v27.0.3+incompatible // indirect
github.com/docker/docker v27.1.0+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions modules/artemis/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down
Loading

0 comments on commit 36fff00

Please sign in to comment.