diff --git a/compose_test.go b/compose_test.go index 835028430e7..917a502cacb 100644 --- a/compose_test.go +++ b/compose_test.go @@ -1,12 +1,15 @@ package testcontainers import ( + "context" "fmt" - "os/exec" + "regexp" "strings" "testing" "time" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -347,13 +350,11 @@ func TestLocalDockerComposeWithEnvironment(t *testing.T) { assert.Equal(t, 1, len(compose.Services)) assert.Contains(t, compose.Services, "nginx") - containerNameNginx := compose.Identifier + "_nginx_1" - present := map[string]string{ "bar": "BAR", } absent := map[string]string{} - assertContainerEnvironmentVariables(t, containerNameNginx, present, absent) + assertContainerEnvironmentVariables(t, compose.Identifier, "nginx", present, absent) } func TestLocalDockerComposeWithMultipleComposeFiles(t *testing.T) { @@ -386,17 +387,18 @@ func TestLocalDockerComposeWithMultipleComposeFiles(t *testing.T) { assert.Contains(t, compose.Services, "mysql") assert.Contains(t, compose.Services, "postgres") - containerNameNginx := compose.Identifier + "_nginx_1" - present := map[string]string{ "bar": "BAR", "foo": "FOO", } absent := map[string]string{} - assertContainerEnvironmentVariables(t, containerNameNginx, present, absent) + assertContainerEnvironmentVariables(t, compose.Identifier, "nginx", present, absent) } func TestLocalDockerComposeWithVolume(t *testing.T) { + if providerType == ProviderPodman { + t.Skip("fails for some reason with Podman") + } path := "./testresources/docker-compose-volume.yml" identifier := strings.ToLower(uuid.New().String()) @@ -405,7 +407,7 @@ func TestLocalDockerComposeWithVolume(t *testing.T) { destroyFn := func() { err := compose.Down() checkIfError(t, err) - assertVolumeDoesNotExist(t, fmt.Sprintf("%s_mydata", identifier)) + assertVolumeDoesNotExist(t, identifier, "mydata") } defer destroyFn() @@ -415,33 +417,79 @@ func TestLocalDockerComposeWithVolume(t *testing.T) { checkIfError(t, err) } -func assertVolumeDoesNotExist(t *testing.T, volume string) { - args := []string{"volume", "inspect", volume} +func assertVolumeDoesNotExist(tb testing.TB, composeIdentifier, volume string) { + containerClient, _, err := NewDockerClient() + if err != nil { + tb.Fatalf("Failed to get provider: %v", err) + } - output, _ := executeAndGetOutput("docker", args) - if !strings.Contains(output, "No such volume") { - t.Fatalf("Expected volume %q to not exist", volume) + volumeList, err := containerClient.VolumeList(context.Background(), filters.NewArgs()) + if err != nil { + tb.Fatalf("Failed to list volumes: %v", err) + } + + if len(volumeList.Warnings) > 0 { + tb.Logf("Volume list warnings: %v", volumeList.Warnings) + } + + volumeNameRegexp := regexp.MustCompile(fmt.Sprintf(`^\/?%s(_|-)%s$`, composeIdentifier, volume)) + + for i := range volumeList.Volumes { + if volumeNameRegexp.MatchString(volumeList.Volumes[i].Name) { + tb.Fatalf("Volume should not be present") + } } } -func assertContainerEnvironmentVariables(t *testing.T, containerName string, present map[string]string, absent map[string]string) { - args := []string{"exec", containerName, "env"} +func assertContainerEnvironmentVariables( + tb testing.TB, + composeIdentifier, serviceName string, + present map[string]string, + absent map[string]string, +) { + containerClient, _, err := NewDockerClient() + if err != nil { + tb.Fatalf("Failed to get provider: %v", err) + } + + containers, err := containerClient.ContainerList(context.Background(), types.ContainerListOptions{}) + if err != nil { + tb.Fatalf("Failed to list containers: %v", err) + } else if len(containers) == 0 { + tb.Fatalf("container list empty") + } - output, err := executeAndGetOutput("docker", args) - checkIfError(t, err) + containerNameRegexp := regexp.MustCompile(fmt.Sprintf(`^\/?%s(_|-)%s(_|-)\d$`, composeIdentifier, serviceName)) + var containerID string +containerLoop: + for i := range containers { + c := containers[i] + for j := range c.Names { + if containerNameRegexp.MatchString(c.Names[j]) { + containerID = c.ID + break containerLoop + } + } + } + + details, err := containerClient.ContainerInspect(context.Background(), containerID) + if err != nil { + tb.Fatalf("Failed to inspect container: %v", err) + } for k, v := range present { keyVal := k + "=" + v - assert.Contains(t, output, keyVal) + assert.Contains(tb, details.Config.Env, keyVal) } for k, v := range absent { keyVal := k + "=" + v - assert.NotContains(t, output, keyVal) + assert.NotContains(tb, details.Config.Env, keyVal) } } func checkIfError(t *testing.T, err ExecError) { + t.Helper() if err.Error != nil { t.Fatalf("Failed when running %v: %v", err.Command, err.Error) } @@ -454,13 +502,3 @@ func checkIfError(t *testing.T, err ExecError) { t.Fatalf("An error in Stderr happened when running %v: %v", err.Command, err.Stderr) } } - -func executeAndGetOutput(command string, args []string) (string, ExecError) { - cmd := exec.Command(command, args...) - out, err := cmd.CombinedOutput() - if err != nil { - return string(out), ExecError{Error: err} - } - - return string(out), ExecError{Error: nil} -} diff --git a/container_test.go b/container_test.go index f0a3239f409..8dccf8a722d 100644 --- a/container_test.go +++ b/container_test.go @@ -26,7 +26,7 @@ func Test_ContainerValidation(t *testing.T) { } testTable := []ContainerValidationTestCase{ - ContainerValidationTestCase{ + { Name: "cannot set both context and image", ExpectedError: errors.New("you cannot specify both an Image and Context in a ContainerRequest"), ContainerRequest: ContainerRequest{ @@ -36,14 +36,14 @@ func Test_ContainerValidation(t *testing.T) { Image: "redis:latest", }, }, - ContainerValidationTestCase{ + { Name: "can set image without context", ExpectedError: nil, ContainerRequest: ContainerRequest{ Image: "redis:latest", }, }, - ContainerValidationTestCase{ + { Name: "can set context without image", ExpectedError: nil, ContainerRequest: ContainerRequest{ @@ -52,7 +52,7 @@ func Test_ContainerValidation(t *testing.T) { }, }, }, - ContainerValidationTestCase{ + { Name: "Can mount same source to multiple targets", ExpectedError: nil, ContainerRequest: ContainerRequest{ @@ -60,7 +60,7 @@ func Test_ContainerValidation(t *testing.T) { Mounts: Mounts(BindMount("/data", "/srv"), BindMount("/data", "/data")), }, }, - ContainerValidationTestCase{ + { Name: "Cannot mount multiple sources to same target", ExpectedError: errors.New("duplicate mount target detected: /data"), ContainerRequest: ContainerRequest{ @@ -149,7 +149,7 @@ func Test_BuildImageWithContexts(t *testing.T) { }{ { Name: "Dockerfile", - Contents: `FROM alpine + Contents: `FROM docker.io/alpine CMD ["echo", "this is from the archive"]`, }, } @@ -197,7 +197,7 @@ func Test_BuildImageWithContexts(t *testing.T) { }, { Name: "Dockerfile", - Contents: `FROM alpine + Contents: `FROM docker.io/alpine WORKDIR /app COPY . . CMD ["sh", "./say_hi.sh"]`, @@ -280,16 +280,14 @@ func Test_BuildImageWithContexts(t *testing.T) { } else { c.Terminate(ctx) } - }) - } } func Test_GetLogsFromFailedContainer(t *testing.T) { ctx := context.Background() req := ContainerRequest{ - Image: "alpine", + Image: "docker.io/alpine", Cmd: []string{"echo", "-n", "I was not expecting this"}, WaitingFor: wait.ForLog("I was expecting this").WithStartupTimeout(5 * time.Second), } @@ -336,8 +334,8 @@ func TestShouldStartContainersInParallel(t *testing.T) { func createTestContainer(t *testing.T, ctx context.Context) int { req := ContainerRequest{ - Image: "nginx", - ExposedPorts: []string{"80/tcp"}, + Image: nginxAlpineImage, + ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForHTTP("/"), } container, err := GenericContainer(ctx, GenericContainerRequest{ @@ -347,7 +345,7 @@ func createTestContainer(t *testing.T, ctx context.Context) int { if err != nil { t.Fatalf("could not start container: %v", err) } - port, err := container.MappedPort(ctx, "80") + port, err := container.MappedPort(ctx, nginxDefaultPort) if err != nil { t.Fatalf("could not get mapped port: %v", err) } diff --git a/docker.go b/docker.go index 80cd84c7e8b..a8aafe51322 100644 --- a/docker.go +++ b/docker.go @@ -613,20 +613,9 @@ func WithDefaultBridgeNetwork(bridgeNetworkName string) DockerProviderOption { }) } -// NewDockerProvider creates a Docker provider with the EnvClient -func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error) { +func NewDockerClient() (cli *client.Client, host string, err error) { tcConfig := readTCPropsFile() - host := tcConfig.Host - - o := &DockerProviderOptions{ - GenericProviderOptions: &GenericProviderOptions{ - Logger: Logger, - }, - } - - for idx := range provOpts { - provOpts[idx].ApplyDockerTo(o) - } + host = tcConfig.Host opts := []client.Opt{client.FromEnv} if host != "" { @@ -646,7 +635,30 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error host = "unix:///var/run/docker.sock" } - c, err := client.NewClientWithOpts(opts...) + cli, err = client.NewClientWithOpts(opts...) + + if err != nil { + return nil, "", err + } + + cli.NegotiateAPIVersion(context.Background()) + + return cli, host, nil +} + +// NewDockerProvider creates a Docker provider with the EnvClient +func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error) { + o := &DockerProviderOptions{ + GenericProviderOptions: &GenericProviderOptions{ + Logger: Logger, + }, + } + + for idx := range provOpts { + provOpts[idx].ApplyDockerTo(o) + } + + c, host, err := NewDockerClient() if err != nil { return nil, err } diff --git a/docker_test.go b/docker_test.go index 1e4840581fb..a8dd984806e 100644 --- a/docker_test.go +++ b/docker_test.go @@ -3,14 +3,12 @@ package testcontainers import ( "context" "database/sql" - "encoding/json" "errors" "fmt" "io/ioutil" "math/rand" "net/http" "os" - "os/exec" "path/filepath" "regexp" "strings" @@ -34,7 +32,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" - "github.com/docker/go-connections/nat" "github.com/go-redis/redis" "github.com/testcontainers/testcontainers-go/wait" @@ -43,6 +40,8 @@ import ( const ( nginxImage = "docker.io/nginx" nginxAlpineImage = "docker.io/nginx:alpine" + nginxDefaultPort = "80/tcp" + nginxHighPort = "8080/tcp" ) var providerType = ProviderDocker @@ -54,23 +53,21 @@ func init() { } func TestContainerAttachedToNewNetwork(t *testing.T) { - t.Parallel() + aliases := []string{"alias1", "alias2", "alias3"} networkName := "new-network" ctx := context.Background() gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, Networks: []string{ networkName, }, NetworkAliases: map[string][]string{ - networkName: { - "alias1", "alias2", "alias3", - }, + networkName: aliases, }, }, } @@ -85,13 +82,14 @@ func TestContainerAttachedToNewNetwork(t *testing.T) { if err != nil { t.Fatal(err) } - defer newNetwork.Remove(ctx) + t.Cleanup(func() { + require.NoError(t, newNetwork.Remove(ctx)) + }) nginx, err := GenericContainer(ctx, gcr) - if err != nil { - t.Fatal(err) - } - defer nginx.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginx) networks, err := nginx.Networks(ctx) if err != nil { @@ -112,48 +110,49 @@ func TestContainerAttachedToNewNetwork(t *testing.T) { if len(networkAliases) != 1 { t.Errorf("Expected network aliases for 1 network. Got '%d'.", len(networkAliases)) } + networkAlias := networkAliases[networkName] - if len(networkAlias) != 3 { - t.Errorf("Expected network aliases %d. Got '%d'.", 3, len(networkAlias)) - } - if networkAlias[0] != "alias1" || networkAlias[1] != "alias2" || networkAlias[2] != "alias3" { - t.Errorf( - "Expected network aliases '%s', '%s' and '%s'. Got '%s', '%s' and '%s'.", - "alias1", "alias2", "alias3", networkAlias[0], networkAlias[1], networkAlias[2]) + + require.NotEmpty(t, networkAlias) + + for _, alias := range aliases { + require.Contains(t, networkAlias, alias) } } func TestContainerWithHostNetworkOptions(t *testing.T) { - t.Parallel() + absPath, err := filepath.Abs("./testresources/nginx-highport.conf") + if err != nil { + t.Fatal(err) + } + ctx := context.Background() gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, Privileged: true, SkipReaper: true, NetworkMode: "host", + Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), ExposedPorts: []string{ - "80/tcp", + nginxHighPort, }, - WaitingFor: wait.ForListeningPort("80/tcp"), + WaitingFor: wait.ForListeningPort(nginxHighPort), }, Started: true, } nginxC, err := GenericContainer(ctx, gcr) - if err != nil { - t.Fatal(err) - } - - defer nginxC.Terminate(ctx) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) // host, err := nginxC.Host(ctx) // if err != nil { // t.Errorf("Expected host %s. Got '%d'.", host, err) // } // - endpoint, err := nginxC.Endpoint(ctx, "http") + endpoint, err := nginxC.PortEndpoint(ctx, nginxHighPort, "http") if err != nil { t.Errorf("Expected server endpoint. Got '%v'.", err) } @@ -186,95 +185,104 @@ func TestContainerWithNetworkModeAndNetworkTogether(t *testing.T) { } func TestContainerWithHostNetworkOptionsAndWaitStrategy(t *testing.T) { - t.Parallel() ctx := context.Background() + absPath, err := filepath.Abs("./testresources/nginx-highport.conf") + if err != nil { + t.Fatal(err) + } + gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, SkipReaper: true, NetworkMode: "host", - WaitingFor: wait.ForListeningPort("80/tcp"), + WaitingFor: wait.ForListeningPort(nginxHighPort), + Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), }, Started: true, } nginxC, err := GenericContainer(ctx, gcr) - if err != nil { - t.Fatal(err) - } - defer nginxC.Terminate(ctx) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) host, err := nginxC.Host(ctx) if err != nil { t.Errorf("Expected host %s. Got '%d'.", host, err) } - _, err = http.Get("http://" + host + ":80") + _, err = http.Get("http://" + host + ":8080") if err != nil { - t.Errorf("Expected OK response. Got '%d'.", err) + t.Errorf("Expected OK response. Got '%v'.", err) } } func TestContainerWithHostNetworkAndEndpoint(t *testing.T) { - t.Parallel() - nginxPort := "80/tcp" ctx := context.Background() + + absPath, err := filepath.Abs("./testresources/nginx-highport.conf") + if err != nil { + t.Fatal(err) + } + gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, SkipReaper: true, NetworkMode: "host", - WaitingFor: wait.ForListeningPort(nat.Port(nginxPort)), + WaitingFor: wait.ForListeningPort(nginxHighPort), + Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), }, Started: true, } nginxC, err := GenericContainer(ctx, gcr) - if err != nil { - t.Fatal(err) - } - defer nginxC.Terminate(ctx) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) - hostN, err := nginxC.Endpoint(ctx, "") + hostN, err := nginxC.PortEndpoint(ctx, nginxHighPort, "http") if err != nil { t.Errorf("Expected host %s. Got '%d'.", hostN, err) } t.Log(hostN) - _, err = http.Get("http://" + hostN) + _, err = http.Get(hostN) if err != nil { - t.Errorf("Expected OK response. Got '%d'.", err) + t.Errorf("Expected OK response. Got '%v'.", err) } } func TestContainerWithHostNetworkAndPortEndpoint(t *testing.T) { - t.Parallel() - nginxPort := "80/tcp" ctx := context.Background() + + absPath, err := filepath.Abs("./testresources/nginx-highport.conf") + if err != nil { + t.Fatal(err) + } + gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, SkipReaper: true, NetworkMode: "host", - WaitingFor: wait.ForListeningPort(nat.Port(nginxPort)), + WaitingFor: wait.ForListeningPort(nginxHighPort), + Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), }, Started: true, } nginxC, err := GenericContainer(ctx, gcr) - if err != nil { - t.Fatal(err) - } - defer nginxC.Terminate(ctx) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) - origin, err := nginxC.PortEndpoint(ctx, nat.Port(nginxPort), "http") + origin, err := nginxC.PortEndpoint(ctx, nginxHighPort, "http") if err != nil { t.Errorf("Expected host %s. Got '%d'.", origin, err) } @@ -287,28 +295,26 @@ func TestContainerWithHostNetworkAndPortEndpoint(t *testing.T) { } func TestContainerReturnItsContainerID(t *testing.T) { - t.Parallel() ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, }, }) - if err != nil { - t.Fatal(err) - } - defer nginxA.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxA) + if nginxA.GetContainerID() == "" { t.Errorf("expected a containerID but we got an empty string.") } } func TestContainerStartsWithoutTheReaper(t *testing.T) { - t.Parallel() ctx := context.Background() client, err := client.NewClientWithOpts(client.FromEnv) if err != nil { @@ -319,17 +325,17 @@ func TestContainerStartsWithoutTheReaper(t *testing.T) { container, err = GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, SkipReaper: true, }, Started: true, }) - if err != nil { - t.Fatal(err) - } + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, container) resp, err := client.ContainerList(ctx, types.ContainerListOptions{ Filters: filters.NewArgs(filters.Arg("label", fmt.Sprintf("%s=%s", TestcontainerLabelSessionID, container.SessionID()))), @@ -343,7 +349,6 @@ func TestContainerStartsWithoutTheReaper(t *testing.T) { } func TestContainerStartsWithTheReaper(t *testing.T) { - t.Parallel() ctx := context.Background() client, err := client.NewClientWithOpts(client.FromEnv) if err != nil { @@ -353,9 +358,9 @@ func TestContainerStartsWithTheReaper(t *testing.T) { _, err = GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, }, Started: true, @@ -380,19 +385,14 @@ func TestContainerStartsWithTheReaper(t *testing.T) { } func TestContainerTerminationResetsState(t *testing.T) { - t.Parallel() ctx := context.Background() - client, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { - t.Fatal(err) - } - client.NegotiateAPIVersion(ctx) + nginxA, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, SkipReaper: true, }, @@ -425,19 +425,17 @@ func TestContainerStopWithReaper(t *testing.T) { nginxA, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - _ = nginxA.Terminate(ctx) - }() + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxA) + containerID := nginxA.GetContainerID() resp, err := client.ContainerInspect(ctx, containerID) if err != nil { @@ -474,9 +472,9 @@ func TestContainerTerminationWithReaper(t *testing.T) { nginxA, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, }, Started: true, @@ -513,9 +511,9 @@ func TestContainerTerminationWithoutReaper(t *testing.T) { nginxA, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, SkipReaper: true, }, @@ -543,9 +541,7 @@ func TestContainerTerminationWithoutReaper(t *testing.T) { } func TestContainerTerminationRemovesDockerImage(t *testing.T) { - t.Parallel() t.Run("if not built from Dockerfile", func(t *testing.T) { - t.Parallel() ctx := context.Background() client, err := client.NewClientWithOpts(client.FromEnv) if err != nil { @@ -555,9 +551,9 @@ func TestContainerTerminationRemovesDockerImage(t *testing.T) { container, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, SkipReaper: true, }, @@ -570,14 +566,13 @@ func TestContainerTerminationRemovesDockerImage(t *testing.T) { if err != nil { t.Fatal(err) } - _, _, err = client.ImageInspectWithRaw(ctx, "nginx") + _, _, err = client.ImageInspectWithRaw(ctx, nginxAlpineImage) if err != nil { t.Fatal("nginx image should not have been removed") } }) t.Run("if built from Dockerfile", func(t *testing.T) { - t.Parallel() ctx := context.Background() client, err := client.NewClientWithOpts(client.FromEnv) if err != nil { @@ -618,58 +613,40 @@ func TestContainerTerminationRemovesDockerImage(t *testing.T) { } func TestTwoContainersExposingTheSamePort(t *testing.T) { - t.Parallel() ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - err := nginxA.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxA) nginxB, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, - WaitingFor: wait.ForListeningPort("80/tcp"), + WaitingFor: wait.ForListeningPort(nginxDefaultPort), }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - err := nginxB.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() - ipA, err := nginxA.Host(ctx) - if err != nil { - t.Fatal(err) - } - portA, err := nginxA.MappedPort(ctx, "80/tcp") - if err != nil { - t.Fatal(err) - } - resp, err := http.Get(fmt.Sprintf("http://%s:%s", ipA, portA.Port())) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxB) + + endpointA, err := nginxA.PortEndpoint(ctx, nginxDefaultPort, "http") + require.NoError(t, err) + + resp, err := http.Get(endpointA) if err != nil { t.Fatal(err) } @@ -677,16 +654,9 @@ func TestTwoContainersExposingTheSamePort(t *testing.T) { t.Errorf("Expected status code %d. Got %d.", http.StatusOK, resp.StatusCode) } - ipB, err := nginxB.Host(ctx) - if err != nil { - t.Fatal(err) - } - portB, err := nginxB.MappedPort(ctx, "80") - if err != nil { - t.Fatal(err) - } + endpointB, err := nginxB.PortEndpoint(ctx, nginxDefaultPort, "http") - resp, err = http.Get(fmt.Sprintf("http://%s:%s", ipB, portB.Port())) + resp, err = http.Get(endpointB) if err != nil { t.Fatal(err) } @@ -699,36 +669,25 @@ func TestContainerCreation(t *testing.T) { t.Parallel() ctx := context.Background() - nginxPort := "80/tcp" nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - nginxPort, + nginxDefaultPort, }, - WaitingFor: wait.ForListeningPort("80/tcp"), + WaitingFor: wait.ForListeningPort(nginxDefaultPort), }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - err := nginxC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() - ip, err := nginxC.Host(ctx) - if err != nil { - t.Fatal(err) - } - port, err := nginxC.MappedPort(ctx, "80") - if err != nil { - t.Fatal(err) - } - resp, err := http.Get(fmt.Sprintf("http://%s:%s", ip, port.Port())) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) + + endpoint, err := nginxC.PortEndpoint(ctx, nginxDefaultPort, "http") + require.NoError(t, err) + + resp, err := http.Get(endpoint) if err != nil { t.Fatal(err) } @@ -761,29 +720,24 @@ func TestContainerCreationWithName(t *testing.T) { creationName := fmt.Sprintf("%s_%d", "test_container", time.Now().Unix()) expectedName := "/" + creationName // inspect adds '/' in the beginning - nginxPort := "80/tcp" + nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - nginxPort, + nginxDefaultPort, }, - WaitingFor: wait.ForListeningPort("80/tcp"), + WaitingFor: wait.ForListeningPort(nginxDefaultPort), Name: creationName, Networks: []string{"bridge"}, }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - err := nginxC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) + name, err := nginxC.Name(ctx) if err != nil { t.Fatal(err) @@ -799,18 +753,21 @@ func TestContainerCreationWithName(t *testing.T) { t.Errorf("Expected networks 1. Got '%d'.", len(networks)) } network := networks[0] - if network != "bridge" { - t.Errorf("Expected network name '%s'. Got '%s'.", "bridge", network) - } - ip, err := nginxC.Host(ctx) - if err != nil { - t.Fatal(err) - } - port, err := nginxC.MappedPort(ctx, "80") - if err != nil { - t.Fatal(err) + switch providerType { + case ProviderDocker: + if network != Bridge { + t.Errorf("Expected network name '%s'. Got '%s'.", Bridge, network) + } + case ProviderPodman: + if network != Podman { + t.Errorf("Expected network name '%s'. Got '%s'.", Podman, network) + } } - resp, err := http.Get(fmt.Sprintf("http://%s:%s", ip, port.Port())) + + endpoint, err := nginxC.PortEndpoint(ctx, nginxDefaultPort, "http") + require.NoError(t, err) + + resp, err := http.Get(endpoint) if err != nil { t.Fatal(err) } @@ -823,29 +780,23 @@ func TestContainerCreationAndWaitForListeningPortLongEnough(t *testing.T) { t.Parallel() ctx := context.Background() - nginxPort := "80/tcp" // delayed-nginx will wait 2s before opening port nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: "docker.io/menedev/delayed-nginx:1.15.2", ExposedPorts: []string{ - nginxPort, + nginxDefaultPort, }, - WaitingFor: wait.ForListeningPort("80"), // default startupTimeout is 60s + WaitingFor: wait.ForListeningPort(nginxDefaultPort), // default startupTimeout is 60s }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - err := nginxC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() - origin, err := nginxC.PortEndpoint(ctx, nat.Port(nginxPort), "http") + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) + + origin, err := nginxC.PortEndpoint(ctx, nginxDefaultPort, "http") if err != nil { t.Fatal(err) } @@ -867,9 +818,9 @@ func TestContainerCreationTimesOut(t *testing.T) { ContainerRequest: ContainerRequest{ Image: "docker.io/menedev/delayed-nginx:1.15.2", ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, - WaitingFor: wait.ForListeningPort("80").WithStartupTimeout(1 * time.Second), + WaitingFor: wait.ForListeningPort(nginxDefaultPort).WithStartupTimeout(1 * time.Second), }, Started: true, }) @@ -883,33 +834,25 @@ func TestContainerCreationTimesOut(t *testing.T) { } func TestContainerRespondsWithHttp200ForIndex(t *testing.T) { - t.Parallel() ctx := context.Background() - nginxPort := "80/tcp" // delayed-nginx will wait 2s before opening port nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, + Image: nginxAlpineImage, ExposedPorts: []string{ - nginxPort, + nginxDefaultPort, }, WaitingFor: wait.ForHTTP("/"), }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - err := nginxC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() - origin, err := nginxC.PortEndpoint(ctx, nat.Port(nginxPort), "http") + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) + + origin, err := nginxC.PortEndpoint(ctx, nginxDefaultPort, "http") if err != nil { t.Fatal(err) } @@ -931,18 +874,13 @@ func TestContainerCreationTimesOutWithHttp(t *testing.T) { ContainerRequest: ContainerRequest{ Image: "docker.io/menedev/delayed-nginx:1.15.2", ExposedPorts: []string{ - "80/tcp", + nginxDefaultPort, }, WaitingFor: wait.ForHTTP("/").WithStartupTimeout(1 * time.Second), }, Started: true, }) - defer func() { - err := nginxC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + terminateContainerOnEnd(t, ctx, nginxC) if err == nil { t.Error("Expected timeout") @@ -984,18 +922,14 @@ func TestContainerCreationWaitsForLog(t *testing.T) { }, WaitingFor: wait.ForLog("port: 3306 MySQL Community Server - GPL"), } - mysqlC, _ := GenericContainer(ctx, GenericContainerRequest{ + mysqlC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, }) - defer func() { - t.Log("terminating container") - err := mysqlC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, mysqlC) host, _ := mysqlC.Host(ctx) p, _ := mysqlC.MappedPort(ctx, "3306/tcp") @@ -1022,7 +956,7 @@ func TestContainerCreationWaitsForLog(t *testing.T) { func Test_BuildContainerFromDockerfile(t *testing.T) { t.Parallel() t.Log("getting context") - context := context.Background() + ctx := context.Background() t.Log("got context, creating container request") req := ContainerRequest{ FromDockerfile: FromDockerfile{ @@ -1042,21 +976,14 @@ func Test_BuildContainerFromDockerfile(t *testing.T) { t.Log("creating redis container") - redisC, err := GenericContainer(context, genContainerReq) + redisC, err := GenericContainer(ctx, genContainerReq) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, redisC) t.Log("created redis container") - defer func() { - t.Log("terminating redis container") - err := redisC.Terminate(context) - if err != nil { - t.Fatal(err) - } - t.Log("terminated redis container") - }() - t.Log("getting redis container endpoint") - endpoint, err := redisC.Endpoint(context, "") + endpoint, err := redisC.Endpoint(ctx, "") if err != nil { t.Fatal(err) } @@ -1069,6 +996,7 @@ func Test_BuildContainerFromDockerfile(t *testing.T) { t.Log("pinging redis") pong, err := client.Ping().Result() + require.NoError(t, err) t.Log("received response from redis") @@ -1104,9 +1032,9 @@ func Test_BuildContainerFromDockerfileWithBuildArgs(t *testing.T) { } c, err := GenericContainer(ctx, genContainerReq) - if err != nil { - t.Fatal(err) - } + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, c) ep, err := c.Endpoint(ctx, "http") if err != nil { @@ -1125,19 +1053,9 @@ func Test_BuildContainerFromDockerfileWithBuildArgs(t *testing.T) { assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, ba, string(body)) - - defer func() { - t.Log("terminating container") - err := c.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - t.Log("terminated container") - }() } func Test_BuildContainerFromDockerfileWithBuildLog(t *testing.T) { - t.Parallel() rescueStdout := os.Stderr r, w, _ := os.Pipe() os.Stderr = w @@ -1161,26 +1079,17 @@ func Test_BuildContainerFromDockerfileWithBuildLog(t *testing.T) { } c, err := GenericContainer(ctx, genContainerReq) - if err != nil { - t.Fatal(err) - } - defer func() { - t.Log("terminating container") - err := c.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - t.Log("terminated container") - }() + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, c) _ = w.Close() out, _ := ioutil.ReadAll(r) os.Stdout = rescueStdout temp := strings.Split(string(out), "\n") - if temp[0] != "Step 1/1 : FROM alpine" { - t.Errorf("Expected stout firstline to be %s. Got '%s'.", "Step 1/2 : FROM alpine", temp[0]) + if !regexp.MustCompile(`(?i)^Step\s*1/1\s*:\s*FROM docker.io/alpine$`).MatchString(temp[0]) { + t.Errorf("Expected stdout firstline to be %s. Got '%s'.", "Step 1/1 : FROM docker.io/alpine", temp[0]) } } @@ -1214,25 +1123,18 @@ func TestContainerCreationWaitingForHostPort(t *testing.T) { t.Parallel() ctx := context.Background() req := ContainerRequest{ - Image: nginxImage, - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), + Image: nginxAlpineImage, + ExposedPorts: []string{nginxDefaultPort}, + WaitingFor: wait.ForListeningPort(nginxDefaultPort), } nginx, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, }) - defer func() { - err := nginx.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - t.Log("terminated nginx container") - }() - if err != nil { - t.Fatal(err) - } + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginx) } func TestContainerCreationWaitingForHostPortWithoutBashThrowsAnError(t *testing.T) { @@ -1240,24 +1142,17 @@ func TestContainerCreationWaitingForHostPortWithoutBashThrowsAnError(t *testing. ctx := context.Background() req := ContainerRequest{ Image: nginxAlpineImage, - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), + ExposedPorts: []string{nginxDefaultPort}, + WaitingFor: wait.ForListeningPort(nginxDefaultPort), } nginx, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, }) - defer func() { - err := nginx.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - t.Log("terminated nginx container") - }() - if err != nil { - t.Fatal(err) - } + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginx) } func TestContainerCreationWaitsForLogAndPort(t *testing.T) { @@ -1281,17 +1176,9 @@ func TestContainerCreationWaitsForLogAndPort(t *testing.T) { ContainerRequest: req, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - t.Log("terminating container") - err := mysqlC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, mysqlC) host, _ := mysqlC.Host(ctx) p, _ := mysqlC.MappedPort(ctx, "3306/tcp") @@ -1334,12 +1221,9 @@ func TestCMD(t *testing.T) { ContainerRequest: req, Started: true, }) - if err != nil { - t.Fatal(err) - } - // defer not needed, but keeping it in for consistency - defer c.Terminate(ctx) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, c) } func TestEntrypoint(t *testing.T) { @@ -1365,12 +1249,9 @@ func TestEntrypoint(t *testing.T) { ContainerRequest: req, Started: true, }) - if err != nil { - t.Fatal(err) - } - // defer not needed, but keeping it in for consistency - defer c.Terminate(ctx) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, c) } func TestReadTCPropsFile(t *testing.T) { @@ -1485,7 +1366,7 @@ func TestReadTCPropsFile(t *testing.T) { func ExampleDockerProvider_CreateContainer() { ctx := context.Background() req := ContainerRequest{ - Image: nginxImage, + Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/"), } @@ -1499,7 +1380,7 @@ func ExampleDockerProvider_CreateContainer() { func ExampleContainer_Host() { ctx := context.Background() req := ContainerRequest{ - Image: nginxImage, + Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/"), } @@ -1515,7 +1396,7 @@ func ExampleContainer_Host() { func ExampleContainer_Start() { ctx := context.Background() req := ContainerRequest{ - Image: nginxImage, + Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/"), } @@ -1529,7 +1410,7 @@ func ExampleContainer_Start() { func ExampleContainer_Stop() { ctx := context.Background() req := ContainerRequest{ - Image: nginxImage, + Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/"), } @@ -1544,7 +1425,7 @@ func ExampleContainer_Stop() { func ExampleContainer_MappedPort() { ctx := context.Background() req := ContainerRequest{ - Image: nginxImage, + Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/"), } @@ -1567,11 +1448,11 @@ func TestContainerCreationWithBindAndVolume(t *testing.T) { ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second) defer cnl() // Create a Docker client. - dockerCli, err := client.NewClientWithOpts(client.FromEnv) + dockerCli, _, err := NewDockerClient() if err != nil { t.Fatal(err) } - dockerCli.NegotiateAPIVersion(ctx) + // Create the volume. vol, err := dockerCli.VolumeCreate(ctx, volume.VolumeCreateBody{ Driver: "local", @@ -1580,14 +1461,14 @@ func TestContainerCreationWithBindAndVolume(t *testing.T) { t.Fatal(err) } volumeName := vol.Name - defer func() { + t.Cleanup(func() { ctx, cnl := context.WithTimeout(context.Background(), 5*time.Second) defer cnl() err := dockerCli.VolumeRemove(ctx, volumeName, true) if err != nil { t.Fatal(err) } - }() + }) // Create the container that writes into the mounted volume. bashC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, @@ -1599,17 +1480,9 @@ func TestContainerCreationWithBindAndVolume(t *testing.T) { }, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - ctx, cnl := context.WithTimeout(context.Background(), 5*time.Second) - defer cnl() - err := bashC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + + require.NoError(t, err) + require.NoError(t, bashC.Terminate(ctx)) } func TestContainerWithTmpFs(t *testing.T) { @@ -1626,16 +1499,9 @@ func TestContainerWithTmpFs(t *testing.T) { ContainerRequest: req, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - t.Log("terminating container") - err := container.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, container) path := "/testtmpfs/test.file" @@ -1665,9 +1531,7 @@ func TestContainerWithTmpFs(t *testing.T) { } func TestContainerNonExistentImage(t *testing.T) { - t.Parallel() t.Run("if the image not found don't propagate the error", func(t *testing.T) { - t.Parallel() _, err := GenericContainer(context.Background(), GenericContainerRequest{ ContainerRequest: ContainerRequest{ Image: "postgres:nonexistent-version", @@ -1683,7 +1547,6 @@ func TestContainerNonExistentImage(t *testing.T) { }) t.Run("the context cancellation is propagated to container creation", func(t *testing.T) { - t.Parallel() ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() _, err := GenericContainer(ctx, GenericContainerRequest{ @@ -1702,6 +1565,9 @@ func TestContainerNonExistentImage(t *testing.T) { } func TestContainerCustomPlatformImage(t *testing.T) { + if providerType == ProviderPodman { + t.Skip("Incompatible Docker API version for Podman") + } t.Parallel() t.Run("error with a non-existent platform", func(t *testing.T) { t.Parallel() @@ -1741,15 +1607,10 @@ func TestContainerCustomPlatformImage(t *testing.T) { Started: false, }) - t.Cleanup(func() { - if c != nil { - c.Terminate(ctx) - } - }) - - assert.NoError(t, err) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, c) - dockerCli, err := client.NewEnvClient() + dockerCli, _, err := NewDockerClient() require.NoError(t, err) dockerCli.NegotiateAPIVersion(ctx) @@ -1778,40 +1639,27 @@ func TestContainerWithCustomHostname(t *testing.T) { ContainerRequest: req, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer func() { - t.Log("terminating container") - err := container.Terminate(ctx) - if err != nil { - t.Fatal(err) - } - }() + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, container) + if actualHostname := readHostname(t, container.GetContainerID()); actualHostname != hostname { t.Fatalf("expected hostname %s, got %s", hostname, actualHostname) } } -// TODO: replace with proper API call -func readHostname(t *testing.T, containerId string) string { - command := exec.Command("curl", - "--silent", - "--unix-socket", - "/var/run/docker.sock", - fmt.Sprintf("http://localhost/containers/%s/json", containerId)) - - output, err := command.CombinedOutput() +func readHostname(tb testing.TB, containerId string) string { + containerClient, _, err := NewDockerClient() if err != nil { - t.Fatal(err) + tb.Fatalf("Failed to create Docker client: %v", err) } - var data map[string]interface{} - err = json.Unmarshal(output, &data) + + containerDetails, err := containerClient.ContainerInspect(context.Background(), containerId) if err != nil { - t.Fatal(err) + tb.Fatalf("Failed to inspect container: %v", err) } - config := data["Config"].(map[string]interface{}) - return config["Hostname"].(string) + + return containerDetails.Config.Hostname } func TestDockerContainerCopyFileToContainer(t *testing.T) { @@ -1822,12 +1670,14 @@ func TestDockerContainerCopyFileToContainer(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxImage, - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), + ExposedPorts: []string{nginxDefaultPort}, + WaitingFor: wait.ForListeningPort(nginxDefaultPort), }, Started: true, }) - defer nginxC.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) copiedFileName := "hello_copy.sh" _ = nginxC.CopyFileToContainer(ctx, "./testresources/hello.sh", "/"+copiedFileName, 700) @@ -1847,12 +1697,14 @@ func TestDockerContainerCopyToContainer(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxImage, - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), + ExposedPorts: []string{nginxDefaultPort}, + WaitingFor: wait.ForListeningPort(nginxDefaultPort), }, Started: true, }) - defer nginxC.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) copiedFileName := "hello_copy.sh" @@ -1882,12 +1734,14 @@ func TestDockerContainerCopyFileFromContainer(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxImage, - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), + ExposedPorts: []string{nginxDefaultPort}, + WaitingFor: wait.ForListeningPort(nginxDefaultPort), }, Started: true, }) - defer nginxC.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) copiedFileName := "hello_copy.sh" _ = nginxC.CopyFileToContainer(ctx, "./testresources/hello.sh", "/"+copiedFileName, 700) @@ -1912,19 +1766,20 @@ func TestDockerContainerCopyFileFromContainer(t *testing.T) { } func TestDockerContainerCopyEmptyFileFromContainer(t *testing.T) { - t.Parallel() ctx := context.Background() nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxImage, - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), + ExposedPorts: []string{nginxDefaultPort}, + WaitingFor: wait.ForListeningPort(nginxDefaultPort), }, Started: true, }) - defer nginxC.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) copiedFileName := "hello_copy.sh" _ = nginxC.CopyFileToContainer(ctx, "./testresources/empty.sh", "/"+copiedFileName, 700) @@ -1949,6 +1804,10 @@ func TestDockerContainerCopyEmptyFileFromContainer(t *testing.T) { } func TestDockerContainerResources(t *testing.T) { + if providerType == ProviderPodman { + t.Skip("Rootless Podman does not support setting rlimit") + } + t.Parallel() ctx := context.Background() @@ -1968,18 +1827,18 @@ func TestDockerContainerResources(t *testing.T) { nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: "nginx", - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), + Image: nginxAlpineImage, + ExposedPorts: []string{nginxDefaultPort}, + WaitingFor: wait.ForListeningPort(nginxDefaultPort), Resources: container.Resources{ Ulimits: expected, }, }, Started: true, }) - require.NoError(t, err) - defer nginxC.Terminate(ctx) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) c, err := client.NewClientWithOpts(client.FromEnv) require.NoError(t, err) @@ -2014,10 +1873,10 @@ func TestContainerWithReaperNetwork(t *testing.T) { } req := ContainerRequest{ - Image: nginxImage, - ExposedPorts: []string{"80/tcp"}, + Image: nginxAlpineImage, + ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForAll( - wait.ForListeningPort("80/tcp"), + wait.ForListeningPort(nginxDefaultPort), wait.ForLog("Configuration complete; ready for start up"), ), Networks: networks, @@ -2029,13 +1888,9 @@ func TestContainerWithReaperNetwork(t *testing.T) { Started: true, }) - defer func() { - t.Log("terminating container") - err := nginxC.Terminate(ctx) - assert.Nil(t, err) - }() + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, nginxC) - assert.Nil(t, err) containerId := nginxC.GetContainerID() cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) @@ -2061,10 +1916,9 @@ func TestContainerWithUserID(t *testing.T) { ContainerRequest: req, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer container.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, container) r, err := container.Logs(ctx) if err != nil { @@ -2092,10 +1946,9 @@ func TestContainerWithNoUserID(t *testing.T) { ContainerRequest: req, Started: true, }) - if err != nil { - t.Fatal(err) - } - defer container.Terminate(ctx) + + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, container) r, err := container.Logs(ctx) if err != nil { @@ -2114,11 +1967,11 @@ func TestGetGatewayIP(t *testing.T) { // When using docker-compose with DinD mode, and using host port or http wait strategy // It's need to invoke GetGatewayIP for get the host t.Parallel() - provider, err := NewDockerProvider(WithLogger(TestLogger(t))) + provider, err := providerType.GetProvider(WithLogger(TestLogger(t))) if err != nil { t.Fatal(err) } - ip, err := provider.GetGatewayIP(context.Background()) + ip, err := provider.(*DockerProvider).GetGatewayIP(context.Background()) if err != nil { t.Fatal(err) } @@ -2127,6 +1980,17 @@ func TestGetGatewayIP(t *testing.T) { } } +func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr Container) { + tb.Helper() + if ctr == nil { + return + } + tb.Cleanup(func() { + tb.Log("terminating container") + require.NoError(tb, ctr.Terminate(ctx)) + }) +} + func randomString() string { rand.Seed(time.Now().UnixNano()) chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + diff --git a/logconsumer_test.go b/logconsumer_test.go index b9a06487f16..5fb3f02bf63 100644 --- a/logconsumer_test.go +++ b/logconsumer_test.go @@ -185,6 +185,9 @@ func Test_ShouldRecognizeLogTypes(t *testing.T) { } func TestContainerLogWithErrClosed(t *testing.T) { + if providerType == ProviderPodman { + t.Skip("Docker-in-Docker does not work with rootless Podman") + } // First spin up a docker-in-docker container, then spin up an inner container within that dind container // Logs are being read from the inner container via the dind container's tcp port, which can be briefly // closed to test behaviour in connection-closed situations. diff --git a/testresources/Dockerfile b/testresources/Dockerfile index 9bc1feaac66..bf302c6240b 100644 --- a/testresources/Dockerfile +++ b/testresources/Dockerfile @@ -1 +1 @@ -FROM redis:5.0-alpine \ No newline at end of file +FROM docker.io/redis:5.0-alpine \ No newline at end of file diff --git a/testresources/args.Dockerfile b/testresources/args.Dockerfile index 02606397193..984ef51eee2 100644 --- a/testresources/args.Dockerfile +++ b/testresources/args.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.13-alpine +FROM docker.io/golang:1.13-alpine ARG FOO diff --git a/testresources/buildlog.Dockerfile b/testresources/buildlog.Dockerfile index 67fd3790189..0a9bc82c980 100644 --- a/testresources/buildlog.Dockerfile +++ b/testresources/buildlog.Dockerfile @@ -1 +1 @@ -FROM alpine +FROM docker.io/alpine diff --git a/testresources/docker-compose-complex.yml b/testresources/docker-compose-complex.yml index c2ea7724663..7694a467ac1 100644 --- a/testresources/docker-compose-complex.yml +++ b/testresources/docker-compose-complex.yml @@ -1,11 +1,11 @@ version: '3' services: nginx: - image: nginx:stable-alpine + image: docker.io/nginx:stable-alpine ports: - "9080:80" mysql: - image: mysql:5.7 + image: docker.io/mysql:5.7 environment: - MYSQL_DATABASE=db - MYSQL_ROOT_PASSWORD=my-secret-pw diff --git a/testresources/docker-compose-container-name.yml b/testresources/docker-compose-container-name.yml index 99525f7f7a7..db54e1d82fa 100644 --- a/testresources/docker-compose-container-name.yml +++ b/testresources/docker-compose-container-name.yml @@ -2,7 +2,7 @@ version: '3' services: nginx: container_name: nginxy - image: nginx:stable-alpine + image: docker.io/nginx:stable-alpine environment: bar: ${bar} ports: diff --git a/testresources/docker-compose-no-exposed-ports.yml b/testresources/docker-compose-no-exposed-ports.yml index ba8565c5120..d1e27913e1f 100644 --- a/testresources/docker-compose-no-exposed-ports.yml +++ b/testresources/docker-compose-no-exposed-ports.yml @@ -1,6 +1,6 @@ version: '3' services: nginx: - image: nginx:stable-alpine + image: docker.io/nginx:stable-alpine ports: - "80" diff --git a/testresources/docker-compose-override.yml b/testresources/docker-compose-override.yml index 36727f0e300..87cc7e47222 100644 --- a/testresources/docker-compose-override.yml +++ b/testresources/docker-compose-override.yml @@ -1,13 +1,13 @@ version: '3' services: nginx: - image: nginx:stable-alpine + image: docker.io/nginx:stable-alpine environment: bar: ${bar} foo: ${foo} ports: - "9080:80" mysql: - image: mysql:5.6 + image: docker.io/mysql:5.6 ports: - "13306:3306" diff --git a/testresources/docker-compose-postgres.yml b/testresources/docker-compose-postgres.yml index 74041af5031..47c720560d3 100644 --- a/testresources/docker-compose-postgres.yml +++ b/testresources/docker-compose-postgres.yml @@ -1,6 +1,6 @@ version: '3' services: postgres: - image: postgres:14 + image: docker.io/postgres:14 ports: - "15432:5432" diff --git a/testresources/docker-compose-simple.yml b/testresources/docker-compose-simple.yml index 347e25d1590..104f6c7484c 100644 --- a/testresources/docker-compose-simple.yml +++ b/testresources/docker-compose-simple.yml @@ -1,7 +1,7 @@ version: '3' services: nginx: - image: nginx:stable-alpine + image: docker.io/nginx:stable-alpine environment: bar: ${bar} ports: diff --git a/testresources/docker-compose-volume.yml b/testresources/docker-compose-volume.yml index df31ee2a33e..cc14090c536 100644 --- a/testresources/docker-compose-volume.yml +++ b/testresources/docker-compose-volume.yml @@ -1,7 +1,7 @@ version: '3' services: nginx: - image: nginx:stable-alpine + image: docker.io/nginx:stable-alpine volumes: - type: volume source: mydata diff --git a/testresources/echo.Dockerfile b/testresources/echo.Dockerfile index 36951e1aa60..10ab9febf4e 100644 --- a/testresources/echo.Dockerfile +++ b/testresources/echo.Dockerfile @@ -1,3 +1,3 @@ -FROM alpine +FROM docker.io/alpine CMD ["echo", "this is from the echo test Dockerfile"] \ No newline at end of file diff --git a/testresources/echoserver.Dockerfile b/testresources/echoserver.Dockerfile index aaf835f35a5..546489ffac3 100644 --- a/testresources/echoserver.Dockerfile +++ b/testresources/echoserver.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.13-alpine +FROM docker.io/golang:1.13-alpine WORKDIR /app diff --git a/testresources/nginx-highport.conf b/testresources/nginx-highport.conf new file mode 100644 index 00000000000..557e63f044b --- /dev/null +++ b/testresources/nginx-highport.conf @@ -0,0 +1,43 @@ +server { + listen 8080; + server_name localhost; + + #access_log /var/log/nginx/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} \ No newline at end of file