From a464f026b353bbc97797543db9dce776bcdb6d4a Mon Sep 17 00:00:00 2001 From: Guillaume Date: Tue, 6 Feb 2024 17:13:18 -0500 Subject: [PATCH] Introduce a `testcontainers_test` package to solve some of the docs imports As discussed in #2180, the imported examples from tests for the docs from lack the package prefix before the imports, which leads to the example not being directly copy/paste-able. To solve the issue it was suggested to create a `testcontainers_test` package for the example tests and to convert everything. I did my best to convert all the tests in a way that makes sense, and I extracted any test that would import very internal things that we should not export. Some of the tests had to be slightly rewritten (the lifecycle tests in particular), but they should stay pretty much the same. Test by running all the root unit tests, they should all work as expected. The examples in the docs should all have the packages as prefixes now. --- config_test.go | 9 +- container_ignore_test.go | 34 ++++ container_test.go | 94 ++++------- docker.go | 163 +++++++++--------- docker_auth_config_test.go | 158 +++++++++++++++++ docker_auth_test.go | 182 ++------------------ docker_client_test.go | 8 +- docker_exec_test.go | 19 ++- docker_mounts.go | 6 + docker_test.go | 338 +++++++++++++++++++------------------ file_test.go | 2 + from_dockerfile_test.go | 40 ++--- generic_test.go | 17 +- image_substitutors_test.go | 2 + image_test.go | 11 +- lifecycle_merge_test.go | 99 +++++++++++ lifecycle_test.go | 220 ++++++++---------------- logconsumer_test.go | 124 ++++++-------- mounts_test.go | 58 ++++--- parallel_test.go | 37 ++-- provider.go | 14 +- provider_test.go | 39 ++--- reaper_test.go | 27 +++ testcontainers_test.go | 16 +- testing_test.go | 10 +- 25 files changed, 912 insertions(+), 815 deletions(-) create mode 100644 container_ignore_test.go create mode 100644 docker_auth_config_test.go create mode 100644 lifecycle_merge_test.go diff --git a/config_test.go b/config_test.go index 85a7f8bba7..516b05218e 100644 --- a/config_test.go +++ b/config_test.go @@ -1,10 +1,11 @@ -package testcontainers +package testcontainers_test import ( "testing" "github.com/stretchr/testify/assert" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/internal/config" ) @@ -26,9 +27,9 @@ func TestReadConfig(t *testing.T) { t.Setenv("USERPROFILE", "") // Windows support t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") - cfg := ReadConfig() + cfg := testcontainers.ReadConfig() - expected := TestcontainersConfig{ + expected := testcontainers.TestcontainersConfig{ RyukDisabled: true, Config: config.Config{ RyukDisabled: true, @@ -38,7 +39,7 @@ func TestReadConfig(t *testing.T) { assert.Equal(t, expected, cfg) t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "false") - cfg = ReadConfig() + cfg = testcontainers.ReadConfig() assert.Equal(t, expected, cfg) }) } diff --git a/container_ignore_test.go b/container_ignore_test.go new file mode 100644 index 0000000000..5a37657758 --- /dev/null +++ b/container_ignore_test.go @@ -0,0 +1,34 @@ +// This test is testing very internal logic that should not be exported away from this package. We'll +// leave it in the main testcontainers package. Do not use for user facing examples. +package testcontainers + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseDockerIgnore(t *testing.T) { + testCases := []struct { + filePath string + expectedErr error + expectedExcluded []string + }{ + { + filePath: "./testdata/dockerignore", + expectedErr: nil, + expectedExcluded: []string{"vendor", "foo", "bar"}, + }, + { + filePath: "./testdata", + expectedErr: nil, + expectedExcluded: []string(nil), + }, + } + + for _, testCase := range testCases { + excluded, err := parseDockerIgnore(testCase.filePath) + assert.Equal(t, testCase.expectedErr, err) + assert.Equal(t, testCase.expectedExcluded, excluded) + } +} diff --git a/container_test.go b/container_test.go index b9089aab97..fce195ce42 100644 --- a/container_test.go +++ b/container_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "archive/tar" @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) @@ -23,15 +24,15 @@ func Test_ContainerValidation(t *testing.T) { type ContainerValidationTestCase struct { Name string ExpectedError error - ContainerRequest ContainerRequest + ContainerRequest testcontainers.ContainerRequest } testTable := []ContainerValidationTestCase{ { Name: "cannot set both context and image", ExpectedError: errors.New("you cannot specify both an Image and Context in a ContainerRequest"), - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{ + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: ".", }, Image: "redis:latest", @@ -40,15 +41,15 @@ func Test_ContainerValidation(t *testing.T) { { Name: "can set image without context", ExpectedError: nil, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "redis:latest", }, }, { Name: "can set context without image", ExpectedError: nil, - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{ + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: ".", }, }, @@ -56,7 +57,7 @@ func Test_ContainerValidation(t *testing.T) { { Name: "Can mount same source to multiple targets", ExpectedError: nil, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "redis:latest", HostConfigModifier: func(hc *container.HostConfig) { hc.Binds = []string{"/data:/srv", "/data:/data"} @@ -66,7 +67,7 @@ func Test_ContainerValidation(t *testing.T) { { Name: "Cannot mount multiple sources to same target", ExpectedError: errors.New("duplicate mount target detected: /data"), - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "redis:latest", HostConfigModifier: func(hc *container.HostConfig) { hc.Binds = []string{"/data:/data", "/data:/data"} @@ -76,7 +77,7 @@ func Test_ContainerValidation(t *testing.T) { { Name: "Invalid bind mount", ExpectedError: errors.New("invalid bind mount: /data:/data:/data"), - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "redis:latest", HostConfigModifier: func(hc *container.HostConfig) { hc.Binds = []string{"/data:/data:/data"} @@ -106,27 +107,27 @@ func Test_GetDockerfile(t *testing.T) { type TestCase struct { name string ExpectedDockerfileName string - ContainerRequest ContainerRequest + ContainerRequest testcontainers.ContainerRequest } testTable := []TestCase{ { name: "defaults to \"Dockerfile\" 1", ExpectedDockerfileName: "Dockerfile", - ContainerRequest: ContainerRequest{}, + ContainerRequest: testcontainers.ContainerRequest{}, }, { name: "defaults to \"Dockerfile\" 2", ExpectedDockerfileName: "Dockerfile", - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{}, + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{}, }, }, { name: "will override name", ExpectedDockerfileName: "CustomDockerfile", - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{ + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Dockerfile: "CustomDockerfile", }, }, @@ -278,8 +279,8 @@ func Test_BuildImageWithContexts(t *testing.T) { if err != nil { t.Fatal(err) } - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ ContextArchive: a, Context: testCase.ContextPath, Dockerfile: testCase.Dockerfile, @@ -287,7 +288,7 @@ func Test_BuildImageWithContexts(t *testing.T) { WaitingFor: wait.ForLog(testCase.ExpectedEchoOutput).WithStartupTimeout(1 * time.Minute), } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -308,14 +309,14 @@ func Test_BuildImageWithContexts(t *testing.T) { func Test_GetLogsFromFailedContainer(t *testing.T) { ctx := context.Background() // directDockerHubReference { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/alpine", Cmd: []string{"echo", "-n", "I was not expecting this"}, WaitingFor: wait.ForLog("I was expecting this").WithStartupTimeout(5 * time.Second), } // } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -391,7 +392,7 @@ func TestImageSubstitutors(t *testing.T) { tests := []struct { name string image string // must be a valid image, as the test will try to create a container from it - substitutors []ImageSubstitutor + substitutors []testcontainers.ImageSubstitutor expectedImage string expectedError error }{ @@ -403,19 +404,19 @@ func TestImageSubstitutors(t *testing.T) { { name: "Noop substitutor", image: "alpine", - substitutors: []ImageSubstitutor{NoopImageSubstitutor{}}, + substitutors: []testcontainers.ImageSubstitutor{NoopImageSubstitutor{}}, expectedImage: "alpine", }, { name: "Prepend namespace", image: "alpine", - substitutors: []ImageSubstitutor{dockerImageSubstitutor{}}, + substitutors: []testcontainers.ImageSubstitutor{dockerImageSubstitutor{}}, expectedImage: "docker.io/alpine", }, { name: "Substitution with error", image: "alpine", - substitutors: []ImageSubstitutor{errorSubstitutor{}}, + substitutors: []testcontainers.ImageSubstitutor{errorSubstitutor{}}, expectedImage: "alpine", expectedError: errSubstitution, }, @@ -424,12 +425,12 @@ func TestImageSubstitutors(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: test.image, ImageSubstitutors: test.substitutors, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -447,7 +448,7 @@ func TestImageSubstitutors(t *testing.T) { // enforce the concrete type, as GenericContainer returns an interface, // which will be changed in future implementations of the library - dockerContainer := container.(*DockerContainer) + dockerContainer := container.(*testcontainers.DockerContainer) assert.Equal(t, test.expectedImage, dockerContainer.Image) }) } @@ -462,12 +463,12 @@ func TestShouldStartContainersInParallel(t *testing.T) { t.Run(fmt.Sprintf("iteration_%d", i), func(t *testing.T) { t.Parallel() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForHTTP("/").WithStartupTimeout(10 * time.Second), } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -488,39 +489,14 @@ func TestShouldStartContainersInParallel(t *testing.T) { } } -func TestParseDockerIgnore(t *testing.T) { - testCases := []struct { - filePath string - expectedErr error - expectedExcluded []string - }{ - { - filePath: "./testdata/dockerignore", - expectedErr: nil, - expectedExcluded: []string{"vendor", "foo", "bar"}, - }, - { - filePath: "./testdata", - expectedErr: nil, - expectedExcluded: []string(nil), - }, - } - - for _, testCase := range testCases { - excluded, err := parseDockerIgnore(testCase.filePath) - assert.Equal(t, testCase.expectedErr, err) - assert.Equal(t, testCase.expectedExcluded, excluded) - } -} - func ExampleGenericContainer_withSubstitutors() { ctx := context.Background() // applyImageSubstitutors { - container, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "alpine:latest", - ImageSubstitutors: []ImageSubstitutor{dockerImageSubstitutor{}}, + ImageSubstitutors: []testcontainers.ImageSubstitutor{dockerImageSubstitutor{}}, }, Started: true, }) @@ -538,7 +514,7 @@ func ExampleGenericContainer_withSubstitutors() { // enforce the concrete type, as GenericContainer returns an interface, // which will be changed in future implementations of the library - dockerContainer := container.(*DockerContainer) + dockerContainer := container.(*testcontainers.DockerContainer) fmt.Println(dockerContainer.Image) diff --git a/docker.go b/docker.go index 9af552c3dd..175f2fd18e 100644 --- a/docker.go +++ b/docker.go @@ -893,6 +893,86 @@ func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (st return buildOptions.Tags[0], nil } +// DefaultDockerLifecycleHook returns the default hooks for a Docker container +func (p *DockerProvider) DefaultDockerLifecycleHook(req ContainerRequest, dockerInput *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig) ContainerLifecycleHooks { + return ContainerLifecycleHooks{ + PreCreates: []ContainerRequestHook{ + func(ctx context.Context, req ContainerRequest) error { + return p.preCreateContainerHook(ctx, req, dockerInput, hostConfig, networkingConfig) + }, + }, + PostCreates: []ContainerHook{ + // copy files to container after it's created + func(ctx context.Context, c Container) error { + for _, f := range req.Files { + err := c.CopyFileToContainer(ctx, f.HostFilePath, f.ContainerFilePath, f.FileMode) + if err != nil { + return fmt.Errorf("can't copy %s to container: %w", f.HostFilePath, err) + } + } + + return nil + }, + }, + PostStarts: []ContainerHook{ + // first post-start hook is to produce logs and start log consumers + func(ctx context.Context, c Container) error { + dockerContainer := c.(*DockerContainer) + + logConsumerConfig := req.LogConsumerCfg + if logConsumerConfig == nil { + return nil + } + + for _, consumer := range logConsumerConfig.Consumers { + dockerContainer.followOutput(consumer) + } + + if len(logConsumerConfig.Consumers) > 0 { + return dockerContainer.startLogProduction(ctx, logConsumerConfig.Opts...) + } + return nil + }, + // second post-start hook is to wait for the container to be ready + func(ctx context.Context, c Container) error { + dockerContainer := c.(*DockerContainer) + + // if a Wait Strategy has been specified, wait before returning + if dockerContainer.WaitingFor != nil { + dockerContainer.logger.Printf( + "🚧 Waiting for container id %s image: %s. Waiting for: %+v", + dockerContainer.ID[:12], dockerContainer.Image, dockerContainer.WaitingFor, + ) + if err := dockerContainer.WaitingFor.WaitUntilReady(ctx, c); err != nil { + return err + } + } + + dockerContainer.isRunning = true + + return nil + }, + }, + PreTerminates: []ContainerHook{ + // first pre-terminate hook is to stop the log production + func(ctx context.Context, c Container) error { + logConsumerConfig := req.LogConsumerCfg + + if logConsumerConfig == nil { + return nil + } + if len(logConsumerConfig.Consumers) == 0 { + return nil + } + + dockerContainer := c.(*DockerContainer) + + return dockerContainer.stopLogProduction() + }, + }, + } +} + // CreateContainer fulfills a request for a container without starting it func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerRequest) (Container, error) { var err error @@ -912,7 +992,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque // If default network is not bridge make sure it is attached to the request // as container won't be attached to it automatically // in case of Podman the bridge network is called 'podman' as 'bridge' would conflict - if p.DefaultNetwork != p.defaultBridgeNetworkName { + if p.DefaultNetwork != p.DefaultBridgeNetworkName { isAttached := false for _, net := range req.Networks { if net == p.DefaultNetwork { @@ -1066,82 +1146,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque // default hooks include logger hook and pre-create hook defaultHooks := []ContainerLifecycleHooks{ DefaultLoggingHook(p.Logger), - { - PreCreates: []ContainerRequestHook{ - func(ctx context.Context, req ContainerRequest) error { - return p.preCreateContainerHook(ctx, req, dockerInput, hostConfig, networkingConfig) - }, - }, - PostCreates: []ContainerHook{ - // copy files to container after it's created - func(ctx context.Context, c Container) error { - for _, f := range req.Files { - err := c.CopyFileToContainer(ctx, f.HostFilePath, f.ContainerFilePath, f.FileMode) - if err != nil { - return fmt.Errorf("can't copy %s to container: %w", f.HostFilePath, err) - } - } - - return nil - }, - }, - PostStarts: []ContainerHook{ - // first post-start hook is to produce logs and start log consumers - func(ctx context.Context, c Container) error { - dockerContainer := c.(*DockerContainer) - - logConsumerConfig := req.LogConsumerCfg - if logConsumerConfig == nil { - return nil - } - - for _, consumer := range logConsumerConfig.Consumers { - dockerContainer.followOutput(consumer) - } - - if len(logConsumerConfig.Consumers) > 0 { - return dockerContainer.startLogProduction(ctx, logConsumerConfig.Opts...) - } - return nil - }, - // second post-start hook is to wait for the container to be ready - func(ctx context.Context, c Container) error { - dockerContainer := c.(*DockerContainer) - - // if a Wait Strategy has been specified, wait before returning - if dockerContainer.WaitingFor != nil { - dockerContainer.logger.Printf( - "🚧 Waiting for container id %s image: %s. Waiting for: %+v", - dockerContainer.ID[:12], dockerContainer.Image, dockerContainer.WaitingFor, - ) - if err := dockerContainer.WaitingFor.WaitUntilReady(ctx, c); err != nil { - return err - } - } - - dockerContainer.isRunning = true - - return nil - }, - }, - PreTerminates: []ContainerHook{ - // first pre-terminate hook is to stop the log production - func(ctx context.Context, c Container) error { - logConsumerConfig := req.LogConsumerCfg - - if logConsumerConfig == nil { - return nil - } - if len(logConsumerConfig.Consumers) == 0 { - return nil - } - - dockerContainer := c.(*DockerContainer) - - return dockerContainer.stopLogProduction() - }, - }, - }, + p.DefaultDockerLifecycleHook(req, dockerInput, hostConfig, networkingConfig), } // always prepend default lifecycle hooks to user-defined hooks @@ -1496,8 +1501,8 @@ func (p *DockerProvider) getDefaultNetwork(ctx context.Context, cli client.APICl reaperNetworkExists := false for _, net := range networkResources { - if net.Name == p.defaultBridgeNetworkName { - return p.defaultBridgeNetworkName, nil + if net.Name == p.DefaultBridgeNetworkName { + return p.DefaultBridgeNetworkName, nil } if net.Name == reaperNetwork { diff --git a/docker_auth_config_test.go b/docker_auth_config_test.go new file mode 100644 index 0000000000..9094a01cf1 --- /dev/null +++ b/docker_auth_config_test.go @@ -0,0 +1,158 @@ +// This test is testing very internal logic that should not be exported away from this package. We'll +// leave it in the main testcontainers package. Do not use for user facing examples. +package testcontainers + +import ( + "context" + "path/filepath" + "testing" + + "github.com/cpuguy83/dockercfg" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/testcontainers/testcontainers-go/internal/core" +) + +const exampleAuth = "https://example-auth.com" + +var testDockerConfigDirPath = filepath.Join("testdata", ".docker") + +var indexDockerIO = core.IndexDockerIO + +func TestGetDockerConfig(t *testing.T) { + const expectedErrorMessage = "Expected to find %s in auth configs" + + // Verify that the default docker config file exists before any test in this suite runs. + // Then, we can safely run the tests that rely on it. + defaultCfg, err := dockercfg.LoadDefaultConfig() + require.NoError(t, err) + require.NotEmpty(t, defaultCfg) + + t.Run("without DOCKER_CONFIG env var retrieves default", func(t *testing.T) { + t.Setenv("DOCKER_CONFIG", "") + + cfg, err := getDockerConfig() + require.NoError(t, err) + require.NotEmpty(t, cfg) + + assert.Equal(t, defaultCfg, cfg) + }) + + t.Run("with DOCKER_CONFIG env var pointing to a non-existing file raises error", func(t *testing.T) { + t.Setenv("DOCKER_CONFIG", filepath.Join(testDockerConfigDirPath, "non-existing")) + + cfg, err := getDockerConfig() + require.Error(t, err) + require.Empty(t, cfg) + }) + + t.Run("with DOCKER_CONFIG env var", func(t *testing.T) { + t.Setenv("DOCKER_CONFIG", testDockerConfigDirPath) + + cfg, err := getDockerConfig() + require.NoError(t, err) + require.NotEmpty(t, cfg) + + assert.Len(t, cfg.AuthConfigs, 3) + + authCfgs := cfg.AuthConfigs + + if _, ok := authCfgs[indexDockerIO]; !ok { + t.Errorf(expectedErrorMessage, indexDockerIO) + } + if _, ok := authCfgs["https://example.com"]; !ok { + t.Errorf(expectedErrorMessage, "https://example.com") + } + if _, ok := authCfgs["https://my.private.registry"]; !ok { + t.Errorf(expectedErrorMessage, "https://my.private.registry") + } + }) + + t.Run("DOCKER_AUTH_CONFIG env var takes precedence", func(t *testing.T) { + t.Setenv("DOCKER_AUTH_CONFIG", `{ + "auths": { + "`+exampleAuth+`": {} + }, + "credsStore": "desktop" + }`) + t.Setenv("DOCKER_CONFIG", testDockerConfigDirPath) + + cfg, err := getDockerConfig() + require.NoError(t, err) + require.NotEmpty(t, cfg) + + assert.Len(t, cfg.AuthConfigs, 1) + + authCfgs := cfg.AuthConfigs + + if _, ok := authCfgs[indexDockerIO]; ok { + t.Errorf("Not expected to find %s in auth configs", indexDockerIO) + } + if _, ok := authCfgs[exampleAuth]; !ok { + t.Errorf(expectedErrorMessage, exampleAuth) + } + }) + + t.Run("retrieve auth with DOCKER_AUTH_CONFIG env var", func(t *testing.T) { + base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret + + t.Setenv("DOCKER_AUTH_CONFIG", `{ + "auths": { + "`+exampleAuth+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" } + }, + "credsStore": "desktop" + }`) + + registry, cfg, err := DockerImageAuth(context.Background(), exampleAuth+"/my/image:latest") + require.NoError(t, err) + require.NotEmpty(t, cfg) + + assert.Equal(t, exampleAuth, registry) + assert.Equal(t, "gopher", cfg.Username) + assert.Equal(t, "secret", cfg.Password) + assert.Equal(t, base64, cfg.Auth) + }) + + t.Run("match registry authentication by host", func(t *testing.T) { + base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret + imageReg := "example-auth.com" + imagePath := "/my/image:latest" + + t.Setenv("DOCKER_AUTH_CONFIG", `{ + "auths": { + "`+exampleAuth+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" } + }, + "credsStore": "desktop" + }`) + + registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath) + require.NoError(t, err) + require.NotEmpty(t, cfg) + + assert.Equal(t, imageReg, registry) + assert.Equal(t, "gopher", cfg.Username) + assert.Equal(t, "secret", cfg.Password) + assert.Equal(t, base64, cfg.Auth) + }) + + t.Run("fail to match registry authentication due to invalid host", func(t *testing.T) { + base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret + imageReg := "example-auth.com" + imagePath := "/my/image:latest" + invalidRegistryURL := "://invalid-host" + + t.Setenv("DOCKER_AUTH_CONFIG", `{ + "auths": { + "`+invalidRegistryURL+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" } + }, + "credsStore": "desktop" + }`) + + registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath) + require.Equal(t, err, dockercfg.ErrCredentialsNotFound) + require.Empty(t, cfg) + + assert.Equal(t, imageReg, registry) + }) +} diff --git a/docker_auth_test.go b/docker_auth_test.go index 514cf753c7..8a5d469d24 100644 --- a/docker_auth_test.go +++ b/docker_auth_test.go @@ -1,169 +1,23 @@ -package testcontainers +package testcontainers_test import ( "context" "fmt" "os" - "path/filepath" "testing" - "github.com/cpuguy83/dockercfg" "github.com/docker/docker/api/types" "github.com/docker/docker/client" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/testcontainers/testcontainers-go/internal/core" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) -const exampleAuth = "https://example-auth.com" - -var testDockerConfigDirPath = filepath.Join("testdata", ".docker") - -var indexDockerIO = core.IndexDockerIO - -func TestGetDockerConfig(t *testing.T) { - const expectedErrorMessage = "Expected to find %s in auth configs" - - // Verify that the default docker config file exists before any test in this suite runs. - // Then, we can safely run the tests that rely on it. - defaultCfg, err := dockercfg.LoadDefaultConfig() - require.NoError(t, err) - require.NotEmpty(t, defaultCfg) - - t.Run("without DOCKER_CONFIG env var retrieves default", func(t *testing.T) { - t.Setenv("DOCKER_CONFIG", "") - - cfg, err := getDockerConfig() - require.NoError(t, err) - require.NotEmpty(t, cfg) - - assert.Equal(t, defaultCfg, cfg) - }) - - t.Run("with DOCKER_CONFIG env var pointing to a non-existing file raises error", func(t *testing.T) { - t.Setenv("DOCKER_CONFIG", filepath.Join(testDockerConfigDirPath, "non-existing")) - - cfg, err := getDockerConfig() - require.Error(t, err) - require.Empty(t, cfg) - }) - - t.Run("with DOCKER_CONFIG env var", func(t *testing.T) { - t.Setenv("DOCKER_CONFIG", testDockerConfigDirPath) - - cfg, err := getDockerConfig() - require.NoError(t, err) - require.NotEmpty(t, cfg) - - assert.Len(t, cfg.AuthConfigs, 3) - - authCfgs := cfg.AuthConfigs - - if _, ok := authCfgs[indexDockerIO]; !ok { - t.Errorf(expectedErrorMessage, indexDockerIO) - } - if _, ok := authCfgs["https://example.com"]; !ok { - t.Errorf(expectedErrorMessage, "https://example.com") - } - if _, ok := authCfgs["https://my.private.registry"]; !ok { - t.Errorf(expectedErrorMessage, "https://my.private.registry") - } - }) - - t.Run("DOCKER_AUTH_CONFIG env var takes precedence", func(t *testing.T) { - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "`+exampleAuth+`": {} - }, - "credsStore": "desktop" - }`) - t.Setenv("DOCKER_CONFIG", testDockerConfigDirPath) - - cfg, err := getDockerConfig() - require.NoError(t, err) - require.NotEmpty(t, cfg) - - assert.Len(t, cfg.AuthConfigs, 1) - - authCfgs := cfg.AuthConfigs - - if _, ok := authCfgs[indexDockerIO]; ok { - t.Errorf("Not expected to find %s in auth configs", indexDockerIO) - } - if _, ok := authCfgs[exampleAuth]; !ok { - t.Errorf(expectedErrorMessage, exampleAuth) - } - }) - - t.Run("retrieve auth with DOCKER_AUTH_CONFIG env var", func(t *testing.T) { - base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret - - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "`+exampleAuth+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" } - }, - "credsStore": "desktop" - }`) - - registry, cfg, err := DockerImageAuth(context.Background(), exampleAuth+"/my/image:latest") - require.NoError(t, err) - require.NotEmpty(t, cfg) - - assert.Equal(t, exampleAuth, registry) - assert.Equal(t, "gopher", cfg.Username) - assert.Equal(t, "secret", cfg.Password) - assert.Equal(t, base64, cfg.Auth) - }) - - t.Run("match registry authentication by host", func(t *testing.T) { - base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret - imageReg := "example-auth.com" - imagePath := "/my/image:latest" - - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "`+exampleAuth+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" } - }, - "credsStore": "desktop" - }`) - - registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath) - require.NoError(t, err) - require.NotEmpty(t, cfg) - - assert.Equal(t, imageReg, registry) - assert.Equal(t, "gopher", cfg.Username) - assert.Equal(t, "secret", cfg.Password) - assert.Equal(t, base64, cfg.Auth) - }) - - t.Run("fail to match registry authentication due to invalid host", func(t *testing.T) { - base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret - imageReg := "example-auth.com" - imagePath := "/my/image:latest" - invalidRegistryURL := "://invalid-host" - - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "`+invalidRegistryURL+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" } - }, - "credsStore": "desktop" - }`) - - registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath) - require.Equal(t, err, dockercfg.ErrCredentialsNotFound) - require.Empty(t, cfg) - - assert.Equal(t, imageReg, registry) - }) -} - func TestBuildContainerFromDockerfile(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata", }, AlwaysPullImage: true, // make sure the authentication takes place @@ -180,7 +34,7 @@ func TestBuildContainerFromDockerfile(t *testing.T) { func removeImageFromLocalCache(t *testing.T, image string) { ctx := context.Background() - testcontainersClient, err := NewDockerClientWithOpts(ctx, client.WithVersion(daemonMaxVersion)) + testcontainersClient, err := testcontainers.NewDockerClientWithOpts(ctx, client.WithVersion(daemonMaxVersion)) if err != nil { t.Log("could not create client to cleanup registry: ", err) } @@ -209,8 +63,8 @@ func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata", Dockerfile: "auth.Dockerfile", }, @@ -238,8 +92,8 @@ func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *test ctx := context.Background() - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata", Dockerfile: "auth.Dockerfile", }, @@ -266,14 +120,14 @@ func TestCreateContainerFromPrivateRegistry(t *testing.T) { prepareLocalRegistryWithAuth(t) ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "localhost:5001/redis:5.0-alpine", AlwaysPullImage: true, // make sure the authentication takes place ExposedPorts: []string{"6379/tcp"}, WaitingFor: wait.ForLog("Ready to accept connections"), } - redisContainer, err := GenericContainer(ctx, GenericContainerRequest{ + redisContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -286,7 +140,7 @@ func prepareLocalRegistryWithAuth(t *testing.T) { wd, err := os.Getwd() require.NoError(t, err) // copyDirectoryToContainer { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "registry:2", ExposedPorts: []string{"5001:5000/tcp"}, Env: map[string]string{ @@ -295,7 +149,7 @@ func prepareLocalRegistryWithAuth(t *testing.T) { "REGISTRY_AUTH_HTPASSWD_PATH": "/auth/htpasswd", "REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY": "/data", }, - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: fmt.Sprintf("%s/testdata/auth", wd), ContainerFilePath: "/auth", @@ -309,13 +163,13 @@ func prepareLocalRegistryWithAuth(t *testing.T) { } // } - genContainerReq := GenericContainerRequest{ + genContainerReq := testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, } - registryC, err := GenericContainer(ctx, genContainerReq) + registryC, err := testcontainers.GenericContainer(ctx, genContainerReq) require.NoError(t, err) t.Cleanup(func() { @@ -329,14 +183,14 @@ func prepareLocalRegistryWithAuth(t *testing.T) { t.Cleanup(cancel) } -func prepareRedisImage(ctx context.Context, req ContainerRequest, t *testing.T) (Container, error) { - genContainerReq := GenericContainerRequest{ +func prepareRedisImage(ctx context.Context, req testcontainers.ContainerRequest, t *testing.T) (testcontainers.Container, error) { + genContainerReq := testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, } - redisC, err := GenericContainer(ctx, genContainerReq) + redisC, err := testcontainers.GenericContainer(ctx, genContainerReq) return redisC, err } diff --git a/docker_client_test.go b/docker_client_test.go index 4b582493bb..d96a031ddb 100644 --- a/docker_client_test.go +++ b/docker_client_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -6,12 +6,14 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/testcontainers/testcontainers-go" ) func TestGetDockerInfo(t *testing.T) { t.Run("works", func(t *testing.T) { ctx := context.Background() - c, err := NewDockerClientWithOpts(ctx) + c, err := testcontainers.NewDockerClientWithOpts(ctx) require.NoError(t, err) info, err := c.Info(ctx) @@ -21,7 +23,7 @@ func TestGetDockerInfo(t *testing.T) { t.Run("is goroutine safe", func(t *testing.T) { ctx := context.Background() - c, err := NewDockerClientWithOpts(ctx) + c, err := testcontainers.NewDockerClientWithOpts(ctx) require.NoError(t, err) count := 1024 diff --git a/docker_exec_test.go b/docker_exec_test.go index 7e1cd4bc2f..d2a9e615c9 100644 --- a/docker_exec_test.go +++ b/docker_exec_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -8,16 +8,17 @@ import ( "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" tcexec "github.com/testcontainers/testcontainers-go/exec" ) func TestExecWithMultiplexedResponse(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -74,11 +75,11 @@ func TestExecWithOptions(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -108,11 +109,11 @@ func TestExecWithOptions(t *testing.T) { func TestExecWithMultiplexedStderrResponse(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -136,11 +137,11 @@ func TestExecWithMultiplexedStderrResponse(t *testing.T) { func TestExecWithNonMultiplexedResponse(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, diff --git a/docker_mounts.go b/docker_mounts.go index 2efdb0c7e2..2b457b2023 100644 --- a/docker_mounts.go +++ b/docker_mounts.go @@ -81,6 +81,12 @@ func (s DockerTmpfsMountSource) GetTmpfsOptions() *mount.TmpfsOptions { return s.TmpfsOptions } +// PrepareMounts maps the given []ContainerMount to the corresponding +// []mount.Mount for further processing +func (m ContainerMounts) PrepareMounts() []mount.Mount { + return mapToDockerMounts(m) +} + // mapToDockerMounts maps the given []ContainerMount to the corresponding // []mount.Mount for further processing func mapToDockerMounts(containerMounts ContainerMounts) []mount.Mount { diff --git a/docker_test.go b/docker_test.go index 45e276b1ed..5714b0b26d 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) @@ -36,11 +37,11 @@ const ( daemonMaxVersion = "1.41" ) -var providerType = ProviderDocker +var providerType = testcontainers.ProviderDocker func init() { if strings.Contains(os.Getenv("DOCKER_HOST"), "podman.sock") { - providerType = ProviderPodman + providerType = testcontainers.ProviderPodman } } @@ -50,18 +51,18 @@ func TestContainerWithHostNetworkOptions(t *testing.T) { } ctx := context.Background() - SkipIfDockerDesktop(t, ctx) + testcontainers.SkipIfDockerDesktop(t, ctx) absPath, err := filepath.Abs(filepath.Join("testdata", "nginx-highport.conf")) if err != nil { t.Fatal(err) } - gcr := GenericContainerRequest{ + gcr := testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: absPath, ContainerFilePath: "/etc/nginx/conf.d/default.conf", @@ -79,7 +80,7 @@ func TestContainerWithHostNetworkOptions(t *testing.T) { Started: true, } - nginxC, err := GenericContainer(ctx, gcr) + nginxC, err := testcontainers.GenericContainer(ctx, gcr) require.NoError(t, err) terminateContainerOnEnd(t, ctx, nginxC) @@ -101,8 +102,8 @@ func TestContainerWithHostNetworkOptions(t *testing.T) { func TestContainerWithHostNetworkOptions_UseExposePortsFromImageConfigs(t *testing.T) { ctx := context.Background() - gcr := GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + gcr := testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "nginx", Privileged: true, WaitingFor: wait.ForExposedPort(), @@ -110,7 +111,7 @@ func TestContainerWithHostNetworkOptions_UseExposePortsFromImageConfigs(t *testi Started: true, } - nginxC, err := GenericContainer(ctx, gcr) + nginxC, err := testcontainers.GenericContainer(ctx, gcr) if err != nil { t.Fatal(err) } @@ -140,12 +141,12 @@ func TestContainerWithNetworkModeAndNetworkTogether(t *testing.T) { // skipIfDockerDesktop { ctx := context.Background() - SkipIfDockerDesktop(t, ctx) + testcontainers.SkipIfDockerDesktop(t, ctx) // } - gcr := GenericContainerRequest{ + gcr := testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxImage, Networks: []string{"new-network"}, HostConfigModifier: func(hc *container.HostConfig) { @@ -155,7 +156,7 @@ func TestContainerWithNetworkModeAndNetworkTogether(t *testing.T) { Started: true, } - nginx, err := GenericContainer(ctx, gcr) + nginx, err := testcontainers.GenericContainer(ctx, gcr) if err != nil { // Error when NetworkMode = host and Network = []string{"bridge"} t.Logf("Can't use Network and NetworkMode together, %s\n", err) @@ -169,19 +170,19 @@ func TestContainerWithHostNetwork(t *testing.T) { } ctx := context.Background() - SkipIfDockerDesktop(t, ctx) + testcontainers.SkipIfDockerDesktop(t, ctx) absPath, err := filepath.Abs(filepath.Join("testdata", "nginx-highport.conf")) if err != nil { t.Fatal(err) } - gcr := GenericContainerRequest{ + gcr := testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, WaitingFor: wait.ForListeningPort(nginxHighPort), - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: absPath, ContainerFilePath: "/etc/nginx/conf.d/default.conf", @@ -194,7 +195,7 @@ func TestContainerWithHostNetwork(t *testing.T) { Started: true, } - nginxC, err := GenericContainer(ctx, gcr) + nginxC, err := testcontainers.GenericContainer(ctx, gcr) require.NoError(t, err) terminateContainerOnEnd(t, ctx, nginxC) @@ -223,9 +224,9 @@ func TestContainerWithHostNetwork(t *testing.T) { func TestContainerReturnItsContainerID(t *testing.T) { ctx := context.Background() - nginxA, err := GenericContainer(ctx, GenericContainerRequest{ + nginxA, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -244,9 +245,9 @@ func TestContainerReturnItsContainerID(t *testing.T) { func TestContainerTerminationResetsState(t *testing.T) { ctx := context.Background() - nginxA, err := GenericContainer(ctx, GenericContainerRequest{ + nginxA, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -272,10 +273,10 @@ func TestContainerTerminationResetsState(t *testing.T) { } func TestContainerStateAfterTermination(t *testing.T) { - createContainerFn := func(ctx context.Context) (Container, error) { - return GenericContainer(ctx, GenericContainerRequest{ + createContainerFn := func(ctx context.Context) (testcontainers.Container, error) { + return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -332,15 +333,15 @@ func TestContainerStateAfterTermination(t *testing.T) { func TestContainerTerminationRemovesDockerImage(t *testing.T) { t.Run("if not built from Dockerfile", func(t *testing.T) { ctx := context.Background() - client, err := NewDockerClientWithOpts(ctx) + client, err := testcontainers.NewDockerClientWithOpts(ctx) if err != nil { t.Fatal(err) } defer client.Close() - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -363,20 +364,20 @@ func TestContainerTerminationRemovesDockerImage(t *testing.T) { t.Run("if built from Dockerfile", func(t *testing.T) { ctx := context.Background() - client, err := NewDockerClientWithOpts(ctx) + client, err := testcontainers.NewDockerClientWithOpts(ctx) if err != nil { t.Fatal(err) } defer client.Close() - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: filepath.Join(".", "testdata"), }, ExposedPorts: []string{"6379/tcp"}, WaitingFor: wait.ForLog("Ready to accept connections"), } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -405,9 +406,9 @@ func TestContainerTerminationRemovesDockerImage(t *testing.T) { func TestTwoContainersExposingTheSamePort(t *testing.T) { ctx := context.Background() - nginxA, err := GenericContainer(ctx, GenericContainerRequest{ + nginxA, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -419,9 +420,9 @@ func TestTwoContainersExposingTheSamePort(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, nginxA) - nginxB, err := GenericContainer(ctx, GenericContainerRequest{ + nginxB, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -466,9 +467,9 @@ func TestTwoContainersExposingTheSamePort(t *testing.T) { func TestContainerCreation(t *testing.T) { ctx := context.Background() - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -520,9 +521,9 @@ func TestContainerCreationWithName(t *testing.T) { creationName := fmt.Sprintf("%s_%d", "test_container", time.Now().Unix()) expectedName := "/" + creationName // inspect adds '/' in the beginning - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -553,13 +554,13 @@ func TestContainerCreationWithName(t *testing.T) { } network := networks[0] switch providerType { - case ProviderDocker: - if network != Bridge { - t.Errorf("Expected network name '%s'. Got '%s'.", Bridge, network) + case testcontainers.ProviderDocker: + if network != testcontainers.Bridge { + t.Errorf("Expected network name '%s'. Got '%s'.", testcontainers.Bridge, network) } - case ProviderPodman: - if network != Podman { - t.Errorf("Expected network name '%s'. Got '%s'.", Podman, network) + case testcontainers.ProviderPodman: + if network != testcontainers.Podman { + t.Errorf("Expected network name '%s'. Got '%s'.", testcontainers.Podman, network) } } @@ -581,9 +582,9 @@ func TestContainerCreationAndWaitForListeningPortLongEnough(t *testing.T) { ctx := context.Background() // delayed-nginx will wait 2s before opening port - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxDelayedImage, ExposedPorts: []string{ nginxDefaultPort, @@ -614,9 +615,9 @@ func TestContainerCreationAndWaitForListeningPortLongEnough(t *testing.T) { func TestContainerCreationTimesOut(t *testing.T) { ctx := context.Background() // delayed-nginx will wait 2s before opening port - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxDelayedImage, ExposedPorts: []string{ nginxDefaultPort, @@ -636,9 +637,9 @@ func TestContainerCreationTimesOut(t *testing.T) { func TestContainerRespondsWithHttp200ForIndex(t *testing.T) { ctx := context.Background() - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{ nginxDefaultPort, @@ -669,9 +670,9 @@ func TestContainerRespondsWithHttp200ForIndex(t *testing.T) { func TestContainerCreationTimesOutWithHttp(t *testing.T) { ctx := context.Background() // delayed-nginx will wait 2s before opening port - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxDelayedImage, ExposedPorts: []string{ nginxDefaultPort, @@ -689,7 +690,7 @@ func TestContainerCreationTimesOutWithHttp(t *testing.T) { func TestContainerCreationWaitsForLogContextTimeout(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: mysqlImage, ExposedPorts: []string{"3306/tcp", "33060/tcp"}, Env: map[string]string{ @@ -698,7 +699,7 @@ func TestContainerCreationWaitsForLogContextTimeout(t *testing.T) { }, WaitingFor: wait.ForLog("test context timeout").WithStartupTimeout(1 * time.Second), } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -712,7 +713,7 @@ func TestContainerCreationWaitsForLogContextTimeout(t *testing.T) { func TestContainerCreationWaitsForLog(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: mysqlImage, ExposedPorts: []string{"3306/tcp", "33060/tcp"}, Env: map[string]string{ @@ -721,7 +722,7 @@ func TestContainerCreationWaitsForLog(t *testing.T) { }, WaitingFor: wait.ForLog("port: 3306 MySQL Community Server - GPL"), } - mysqlC, err := GenericContainer(ctx, GenericContainerRequest{ + mysqlC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -740,8 +741,8 @@ func Test_BuildContainerFromDockerfileWithBuildArgs(t *testing.T) { // fromDockerfileWithBuildArgs { ba := "build args value" - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: filepath.Join(".", "testdata"), Dockerfile: "args.Dockerfile", BuildArgs: map[string]*string{ @@ -753,13 +754,13 @@ func Test_BuildContainerFromDockerfileWithBuildArgs(t *testing.T) { } // } - genContainerReq := GenericContainerRequest{ + genContainerReq := testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, genContainerReq) + c, err := testcontainers.GenericContainer(ctx, genContainerReq) require.NoError(t, err) terminateContainerOnEnd(t, ctx, c) @@ -794,8 +795,8 @@ func Test_BuildContainerFromDockerfileWithBuildLog(t *testing.T) { t.Log("got ctx, creating container request") // fromDockerfile { - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: filepath.Join(".", "testdata"), Dockerfile: "buildlog.Dockerfile", PrintBuildLog: true, @@ -803,13 +804,13 @@ func Test_BuildContainerFromDockerfileWithBuildLog(t *testing.T) { } // } - genContainerReq := GenericContainerRequest{ + genContainerReq := testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, genContainerReq) + c, err := testcontainers.GenericContainer(ctx, genContainerReq) require.NoError(t, err) terminateContainerOnEnd(t, ctx, c) @@ -826,7 +827,7 @@ func Test_BuildContainerFromDockerfileWithBuildLog(t *testing.T) { func TestContainerCreationWaitsForLogAndPortContextTimeout(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: mysqlImage, ExposedPorts: []string{"3306/tcp", "33060/tcp"}, Env: map[string]string{ @@ -838,7 +839,7 @@ func TestContainerCreationWaitsForLogAndPortContextTimeout(t *testing.T) { wait.ForListeningPort("3306/tcp"), ), } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -853,13 +854,13 @@ func TestContainerCreationWaitsForLogAndPortContextTimeout(t *testing.T) { func TestContainerCreationWaitingForHostPort(t *testing.T) { ctx := context.Background() // exposePorts { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), } // } - nginx, err := GenericContainer(ctx, GenericContainerRequest{ + nginx, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -871,12 +872,12 @@ func TestContainerCreationWaitingForHostPort(t *testing.T) { func TestContainerCreationWaitingForHostPortWithoutBashThrowsAnError(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), } - nginx, err := GenericContainer(ctx, GenericContainerRequest{ + nginx, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -895,7 +896,7 @@ func TestCMD(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/alpine", WaitingFor: wait.ForAll( wait.ForLog("command override!"), @@ -903,7 +904,7 @@ func TestCMD(t *testing.T) { Cmd: []string{"echo", "command override!"}, } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -922,7 +923,7 @@ func TestEntrypoint(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/alpine", WaitingFor: wait.ForAll( wait.ForLog("entrypoint override!"), @@ -930,7 +931,7 @@ func TestEntrypoint(t *testing.T) { Entrypoint: []string{"echo", "entrypoint override!"}, } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -949,7 +950,7 @@ func TestWorkingDir(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/alpine", WaitingFor: wait.ForAll( wait.ForLog("/var/tmp/test"), @@ -958,7 +959,7 @@ func TestWorkingDir(t *testing.T) { WorkingDir: "/var/tmp/test", } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -970,12 +971,12 @@ func TestWorkingDir(t *testing.T) { func ExampleDockerProvider_CreateContainer() { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/").WithStartupTimeout(10 * time.Second), } - nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ + nginxC, _ := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -998,12 +999,12 @@ func ExampleDockerProvider_CreateContainer() { func ExampleContainer_Host() { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/").WithStartupTimeout(10 * time.Second), } - nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ + nginxC, _ := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -1030,12 +1031,12 @@ func ExampleContainer_Host() { func ExampleContainer_Start() { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/").WithStartupTimeout(10 * time.Second), } - nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ + nginxC, _ := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, }) defer func() { @@ -1058,12 +1059,12 @@ func ExampleContainer_Start() { func ExampleContainer_Stop() { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/").WithStartupTimeout(10 * time.Second), } - nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ + nginxC, _ := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, }) defer func() { @@ -1087,12 +1088,12 @@ func ExampleContainer_Stop() { func ExampleContainer_MappedPort() { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/nginx:alpine", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForHTTP("/").WithStartupTimeout(10 * time.Second), } - nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ + nginxC, _ := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -1130,17 +1131,17 @@ func TestContainerCreationWithVolumeAndFileWritingToIt(t *testing.T) { volumeName := "volumeName" // Create the container that writes into the mounted volume. - bashC, err := GenericContainer(ctx, GenericContainerRequest{ + bashC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/bash", - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: absPath, ContainerFilePath: "/hello.sh", }, }, - Mounts: Mounts(VolumeMount(volumeName, "/data")), + Mounts: testcontainers.Mounts(testcontainers.VolumeMount(volumeName, "/data")), Cmd: []string{"bash", "/hello.sh"}, WaitingFor: wait.ForLog("done"), }, @@ -1153,13 +1154,13 @@ func TestContainerCreationWithVolumeAndFileWritingToIt(t *testing.T) { func TestContainerWithTmpFs(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/busybox", Cmd: []string{"sleep", "10"}, Tmpfs: map[string]string{"/testtmpfs": "rw"}, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -1210,8 +1211,8 @@ func TestContainerWithTmpFs(t *testing.T) { func TestContainerNonExistentImage(t *testing.T) { t.Run("if the image not found don't propagate the error", func(t *testing.T) { - _, err := GenericContainer(context.Background(), GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + _, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "postgres:nonexistent-version", }, Started: true, @@ -1226,9 +1227,9 @@ func TestContainerNonExistentImage(t *testing.T) { t.Run("the context cancellation is propagated to container creation", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/postgres:12", WaitingFor: wait.ForLog("log"), }, @@ -1243,7 +1244,7 @@ func TestContainerNonExistentImage(t *testing.T) { } func TestContainerCustomPlatformImage(t *testing.T) { - if providerType == ProviderPodman { + if providerType == testcontainers.ProviderPodman { t.Skip("Incompatible Docker API version for Podman") } t.Run("error with a non-existent platform", func(t *testing.T) { @@ -1251,9 +1252,9 @@ func TestContainerCustomPlatformImage(t *testing.T) { nonExistentPlatform := "windows/arm12" ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/redis:latest", ImagePlatform: nonExistentPlatform, }, @@ -1269,9 +1270,9 @@ func TestContainerCustomPlatformImage(t *testing.T) { t.Parallel() ctx := context.Background() - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/mysql:8.0.36", ImagePlatform: "linux/amd64", }, @@ -1281,7 +1282,7 @@ func TestContainerCustomPlatformImage(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, c) - dockerCli, err := NewDockerClientWithOpts(ctx) + dockerCli, err := testcontainers.NewDockerClientWithOpts(ctx) require.NoError(t, err) defer dockerCli.Close() @@ -1299,12 +1300,12 @@ func TestContainerWithCustomHostname(t *testing.T) { ctx := context.Background() name := fmt.Sprintf("some-nginx-%s-%d", t.Name(), rand.Int()) hostname := fmt.Sprintf("my-nginx-%s-%d", t.Name(), rand.Int()) - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Name: name, Image: nginxImage, Hostname: hostname, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -1319,7 +1320,7 @@ func TestContainerWithCustomHostname(t *testing.T) { } func readHostname(tb testing.TB, containerId string) string { - containerClient, err := NewDockerClientWithOpts(context.Background()) + containerClient, err := testcontainers.NewDockerClientWithOpts(context.Background()) if err != nil { tb.Fatalf("Failed to create Docker client: %v", err) } @@ -1352,9 +1353,9 @@ func TestDockerContainerCopyFileToContainer(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -1380,9 +1381,9 @@ func TestDockerContainerCopyFileToContainer(t *testing.T) { func TestDockerContainerCopyDirToContainer(t *testing.T) { ctx := context.Background() - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -1412,12 +1413,12 @@ func TestDockerCreateContainerWithFiles(t *testing.T) { copiedFileName := "/hello_copy.sh" tests := []struct { name string - files []ContainerFile + files []testcontainers.ContainerFile errMsg string }{ { name: "success copy", - files: []ContainerFile{ + files: []testcontainers.ContainerFile{ { HostFilePath: hostFileName, ContainerFilePath: copiedFileName, @@ -1427,7 +1428,7 @@ func TestDockerCreateContainerWithFiles(t *testing.T) { }, { name: "host file not found", - files: []ContainerFile{ + files: []testcontainers.ContainerFile{ { HostFilePath: hostFileName + "123", ContainerFilePath: copiedFileName, @@ -1441,8 +1442,8 @@ func TestDockerCreateContainerWithFiles(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "nginx:1.17.6", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForListeningPort("80/tcp"), @@ -1483,12 +1484,12 @@ func TestDockerCreateContainerWithDirs(t *testing.T) { tests := []struct { name string - dir ContainerFile + dir testcontainers.ContainerFile hasError bool }{ { name: "success copy directory with full path", - dir: ContainerFile{ + dir: testcontainers.ContainerFile{ HostFilePath: abs, ContainerFilePath: "/tmp/" + hostDirName, // the parent dir must exist FileMode: 700, @@ -1497,7 +1498,7 @@ func TestDockerCreateContainerWithDirs(t *testing.T) { }, { name: "success copy directory", - dir: ContainerFile{ + dir: testcontainers.ContainerFile{ HostFilePath: filepath.Join(".", hostDirName), ContainerFilePath: "/tmp/" + hostDirName, // the parent dir must exist FileMode: 700, @@ -1506,7 +1507,7 @@ func TestDockerCreateContainerWithDirs(t *testing.T) { }, { name: "host dir not found", - dir: ContainerFile{ + dir: testcontainers.ContainerFile{ HostFilePath: filepath.Join(".", "testdata123"), // does not exist ContainerFilePath: "/tmp/" + hostDirName, // the parent dir must exist FileMode: 700, @@ -1515,7 +1516,7 @@ func TestDockerCreateContainerWithDirs(t *testing.T) { }, { name: "container dir not found", - dir: ContainerFile{ + dir: testcontainers.ContainerFile{ HostFilePath: filepath.Join(".", hostDirName), ContainerFilePath: "/parent-does-not-exist/testdata123", // does not exist FileMode: 700, @@ -1526,12 +1527,12 @@ func TestDockerCreateContainerWithDirs(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "nginx:1.17.6", ExposedPorts: []string{"80/tcp"}, WaitingFor: wait.ForListeningPort("80/tcp"), - Files: []ContainerFile{tc.dir}, + Files: []testcontainers.ContainerFile{tc.dir}, }, Started: false, }) @@ -1566,9 +1567,9 @@ func TestDockerContainerCopyToContainer(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -1605,9 +1606,9 @@ func TestDockerContainerCopyFileFromContainer(t *testing.T) { } ctx := context.Background() - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -1644,9 +1645,9 @@ func TestDockerContainerCopyFileFromContainer(t *testing.T) { func TestDockerContainerCopyEmptyFileFromContainer(t *testing.T) { ctx := context.Background() - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -1681,7 +1682,7 @@ func TestDockerContainerCopyEmptyFileFromContainer(t *testing.T) { } func TestDockerContainerResources(t *testing.T) { - if providerType == ProviderPodman { + if providerType == testcontainers.ProviderPodman { t.Skip("Rootless Podman does not support setting rlimit") } if os.Getenv("XDG_RUNTIME_DIR") != "" { @@ -1703,9 +1704,9 @@ func TestDockerContainerResources(t *testing.T) { }, } - nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -1721,7 +1722,7 @@ func TestDockerContainerResources(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, nginxC) - c, err := NewDockerClientWithOpts(ctx) + c, err := testcontainers.NewDockerClientWithOpts(ctx) require.NoError(t, err) defer c.Close() @@ -1734,7 +1735,7 @@ func TestDockerContainerResources(t *testing.T) { } func TestContainerCapAdd(t *testing.T) { - if providerType == ProviderPodman { + if providerType == testcontainers.ProviderPodman { t.Skip("Rootless Podman does not support setting cap-add/cap-drop") } @@ -1742,9 +1743,9 @@ func TestContainerCapAdd(t *testing.T) { expected := "IPC_LOCK" - nginx, err := GenericContainer(ctx, GenericContainerRequest{ + nginx, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -1757,7 +1758,7 @@ func TestContainerCapAdd(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, nginx) - dockerClient, err := NewDockerClientWithOpts(ctx) + dockerClient, err := testcontainers.NewDockerClientWithOpts(ctx) require.NoError(t, err) defer dockerClient.Close() @@ -1770,7 +1771,7 @@ func TestContainerCapAdd(t *testing.T) { func TestContainerRunningCheckingStatusCode(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "influxdb:1.8.10-alpine", ExposedPorts: []string{"8086/tcp"}, ImagePlatform: "linux/amd64", // influxdb doesn't provide an alpine+arm build (https://github.com/influxdata/influxdata-docker/issues/335) @@ -1783,7 +1784,7 @@ func TestContainerRunningCheckingStatusCode(t *testing.T) { ), } - influx, err := GenericContainer(ctx, GenericContainerRequest{ + influx, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -1796,13 +1797,13 @@ func TestContainerRunningCheckingStatusCode(t *testing.T) { func TestContainerWithUserID(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/alpine:latest", User: "60125", Cmd: []string{"sh", "-c", "id -u"}, WaitingFor: wait.ForExit(), } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -1826,12 +1827,12 @@ func TestContainerWithUserID(t *testing.T) { func TestContainerWithNoUserID(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/alpine:latest", Cmd: []string{"sh", "-c", "id -u"}, WaitingFor: wait.ForExit(), } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, @@ -1856,13 +1857,13 @@ func TestContainerWithNoUserID(t *testing.T) { 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 - provider, err := providerType.GetProvider(WithLogger(TestLogger(t))) + provider, err := providerType.GetProvider(testcontainers.WithLogger(testcontainers.TestLogger(t))) if err != nil { t.Fatal(err) } defer provider.Close() - ip, err := provider.(*DockerProvider).GetGatewayIP(context.Background()) + ip, err := provider.(*testcontainers.DockerProvider).GetGatewayIP(context.Background()) if err != nil { t.Fatal(err) } @@ -1872,7 +1873,7 @@ func TestGetGatewayIP(t *testing.T) { } func TestProviderHasConfig(t *testing.T) { - provider, err := NewDockerProvider(WithLogger(TestLogger(t))) + provider, err := testcontainers.NewDockerProvider(testcontainers.WithLogger(testcontainers.TestLogger(t))) if err != nil { t.Fatal(err) } @@ -1883,9 +1884,9 @@ func TestProviderHasConfig(t *testing.T) { func TestNetworkModeWithContainerReference(t *testing.T) { ctx := context.Background() - nginxA, err := GenericContainer(ctx, GenericContainerRequest{ + nginxA, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, }, Started: true, @@ -1895,9 +1896,9 @@ func TestNetworkModeWithContainerReference(t *testing.T) { terminateContainerOnEnd(t, ctx, nginxA) networkMode := fmt.Sprintf("container:%v", nginxA.GetContainerID()) - nginxB, err := GenericContainer(ctx, GenericContainerRequest{ + nginxB, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, HostConfigModifier: func(hc *container.HostConfig) { hc.NetworkMode = container.NetworkMode(networkMode) @@ -1911,7 +1912,7 @@ func TestNetworkModeWithContainerReference(t *testing.T) { } // creates a temporary dir in which the files will be extracted. Then it will compare the bytes of each file in the source with the bytes from the copied-from-container file -func assertExtractedFiles(t *testing.T, ctx context.Context, container Container, hostFilePath string, containerFilePath string) { +func assertExtractedFiles(t *testing.T, ctx context.Context, container testcontainers.Container, hostFilePath string, containerFilePath string) { // create all copied files into a temporary dir tmpDir := t.TempDir() @@ -1955,7 +1956,7 @@ func assertExtractedFiles(t *testing.T, ctx context.Context, container Container } } -func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr Container) { +func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr testcontainers.Container) { tb.Helper() if ctr == nil { return @@ -1966,15 +1967,15 @@ func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr Container) }) } -func TestDockerProviderFindContainerByName(t *testing.T) { +func TestDockerProviderReuseOrCreateContainer(t *testing.T) { ctx := context.Background() - provider, err := NewDockerProvider(WithLogger(TestLogger(t))) + provider, err := testcontainers.NewDockerProvider(testcontainers.WithLogger(testcontainers.TestLogger(t))) require.NoError(t, err) defer provider.Close() - c1, err := GenericContainer(ctx, GenericContainerRequest{ + c1, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Name: "test", Image: "nginx:1.17.6", WaitingFor: wait.ForExposedPort(), @@ -1986,9 +1987,9 @@ func TestDockerProviderFindContainerByName(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, c1) - c2, err := GenericContainer(ctx, GenericContainerRequest{ + c2, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Name: "test2", Image: "nginx:1.17.6", WaitingFor: wait.ForExposedPort(), @@ -1998,10 +1999,13 @@ func TestDockerProviderFindContainerByName(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, c2) - c, err := provider.findContainerByName(ctx, "test") + c, err := provider.ReuseOrCreateContainer(ctx, testcontainers.ContainerRequest{ + Name: "test", + }) + foundName, err := c.Name(ctx) require.NoError(t, err) require.NotNil(t, c) - assert.Contains(t, c.Names, c1Name) + assert.Contains(t, foundName, c1Name) } func TestImageBuiltFromDockerfile_KeepBuiltImage(t *testing.T) { @@ -2016,15 +2020,15 @@ func TestImageBuiltFromDockerfile_KeepBuiltImage(t *testing.T) { t.Run(fmt.Sprintf("Keep built image: %t", tt.keepBuiltImage), func(t *testing.T) { ctx := context.Background() // Set up CLI. - provider, err := NewDockerProvider() + provider, err := testcontainers.NewDockerProvider() require.NoError(t, err, "get docker provider should not fail") defer func() { _ = provider.Close() }() cli := provider.Client() // Create container. - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{ + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "testdata", Dockerfile: "echo.Dockerfile", KeepImage: tt.keepBuiltImage, diff --git a/file_test.go b/file_test.go index 367e4833bc..c1fc9f0704 100644 --- a/file_test.go +++ b/file_test.go @@ -1,3 +1,5 @@ +// This test is testing very internal logic that should not be exported away from this package. We'll +// leave it in the main testcontainers package. Do not use for user facing examples. package testcontainers import ( diff --git a/from_dockerfile_test.go b/from_dockerfile_test.go index 7576499a64..583620e7d7 100644 --- a/from_dockerfile_test.go +++ b/from_dockerfile_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -12,10 +12,12 @@ import ( "github.com/docker/docker/api/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/testcontainers/testcontainers-go" ) func TestBuildImageFromDockerfile(t *testing.T) { - provider, err := NewDockerProvider() + provider, err := testcontainers.NewDockerProvider() if err != nil { t.Fatal(err) } @@ -25,9 +27,9 @@ func TestBuildImageFromDockerfile(t *testing.T) { ctx := context.Background() - tag, err := provider.BuildImage(ctx, &ContainerRequest{ + tag, err := provider.BuildImage(ctx, &testcontainers.ContainerRequest{ // fromDockerfileIncludingRepo { - FromDockerfile: FromDockerfile{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "testdata", Dockerfile: "echo.Dockerfile", Repo: "test-repo", @@ -53,7 +55,7 @@ func TestBuildImageFromDockerfile(t *testing.T) { } func TestBuildImageFromDockerfile_NoRepo(t *testing.T) { - provider, err := NewDockerProvider() + provider, err := testcontainers.NewDockerProvider() if err != nil { t.Fatal(err) } @@ -63,8 +65,8 @@ func TestBuildImageFromDockerfile_NoRepo(t *testing.T) { ctx := context.Background() - tag, err := provider.BuildImage(ctx, &ContainerRequest{ - FromDockerfile: FromDockerfile{ + tag, err := provider.BuildImage(ctx, &testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "testdata", Dockerfile: "echo.Dockerfile", Repo: "test-repo", @@ -88,7 +90,7 @@ func TestBuildImageFromDockerfile_NoRepo(t *testing.T) { } func TestBuildImageFromDockerfile_NoTag(t *testing.T) { - provider, err := NewDockerProvider() + provider, err := testcontainers.NewDockerProvider() if err != nil { t.Fatal(err) } @@ -98,8 +100,8 @@ func TestBuildImageFromDockerfile_NoTag(t *testing.T) { ctx := context.Background() - tag, err := provider.BuildImage(ctx, &ContainerRequest{ - FromDockerfile: FromDockerfile{ + tag, err := provider.BuildImage(ctx, &testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "testdata", Dockerfile: "echo.Dockerfile", Tag: "test-tag", @@ -126,9 +128,9 @@ func TestBuildImageFromDockerfile_Target(t *testing.T) { // there are thre targets: target0, target1 and target2. for i := 0; i < 3; i++ { ctx := context.Background() - c, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "testdata", Dockerfile: "target.Dockerfile", PrintBuildLog: true, @@ -160,9 +162,9 @@ func ExampleGenericContainer_buildFromDockerfile() { ctx := context.Background() // buildFromDockerfileWithModifier { - c, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "testdata", Dockerfile: "target.Dockerfile", PrintBuildLog: true, @@ -199,9 +201,9 @@ func TestBuildImageFromDockerfile_TargetDoesNotExist(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - _, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ - FromDockerfile: FromDockerfile{ + _, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "testdata", Dockerfile: "target.Dockerfile", PrintBuildLog: true, diff --git a/generic_test.go b/generic_test.go index cb38e29faf..f4d2e0a676 100644 --- a/generic_test.go +++ b/generic_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) @@ -18,9 +19,9 @@ const ( func TestGenericReusableContainer(t *testing.T) { ctx := context.Background() - n1, err := GenericContainer(ctx, GenericContainerRequest{ + n1, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -45,7 +46,7 @@ func TestGenericReusableContainer(t *testing.T) { { name: "reuse option with empty name", errorMatcher: func(err error) error { - if errors.Is(err, ErrReuseEmptyName) { + if errors.Is(err, testcontainers.ErrReuseEmptyName) { return nil } return err @@ -75,9 +76,9 @@ func TestGenericReusableContainer(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - n2, err := GenericContainer(ctx, GenericContainerRequest{ + n2, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, ExposedPorts: []string{nginxDefaultPort}, WaitingFor: wait.ForListeningPort(nginxDefaultPort), @@ -105,9 +106,9 @@ func TestGenericContainerShouldReturnRefOnError(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: nginxAlpineImage, WaitingFor: wait.ForLog("this string should not be present in the logs"), }, diff --git a/image_substitutors_test.go b/image_substitutors_test.go index 64e3b95d2d..ef1ee232ab 100644 --- a/image_substitutors_test.go +++ b/image_substitutors_test.go @@ -1,3 +1,5 @@ +// This test is testing very internal logic that should not be exported away from this package. We'll +// leave it in the main testcontainers package. Do not use for user facing examples. package testcontainers import ( diff --git a/image_test.go b/image_test.go index 17595b6590..4232239bd7 100644 --- a/image_test.go +++ b/image_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -6,13 +6,14 @@ import ( "path/filepath" "testing" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/internal/core" ) func TestImageList(t *testing.T) { t.Setenv("DOCKER_HOST", core.ExtractDockerHost(context.Background())) - provider, err := ProviderDocker.GetProvider() + provider, err := testcontainers.ProviderDocker.GetProvider() if err != nil { t.Fatalf("failed to get provider %v", err) } @@ -21,7 +22,7 @@ func TestImageList(t *testing.T) { _ = provider.Close() }() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "redis:latest", } @@ -56,7 +57,7 @@ func TestImageList(t *testing.T) { func TestSaveImages(t *testing.T) { t.Setenv("DOCKER_HOST", core.ExtractDockerHost(context.Background())) - provider, err := ProviderDocker.GetProvider() + provider, err := testcontainers.ProviderDocker.GetProvider() if err != nil { t.Fatalf("failed to get provider %v", err) } @@ -65,7 +66,7 @@ func TestSaveImages(t *testing.T) { _ = provider.Close() }() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "redis:latest", } diff --git a/lifecycle_merge_test.go b/lifecycle_merge_test.go new file mode 100644 index 0000000000..7d82ba5776 --- /dev/null +++ b/lifecycle_merge_test.go @@ -0,0 +1,99 @@ +// This test is testing very internal logic that should not be exported away from this package. We'll +// leave it in the main testcontainers package. Do not use for user facing examples. +package testcontainers + +import ( + "testing" + + "github.com/docker/go-connections/nat" + "github.com/stretchr/testify/assert" +) + +func TestMergePortBindings(t *testing.T) { + type arg struct { + configPortMap nat.PortMap + parsedPortMap nat.PortMap + exposedPorts []string + } + cases := []struct { + name string + arg arg + expected nat.PortMap + }{ + { + name: "empty ports", + arg: arg{ + configPortMap: nil, + parsedPortMap: nil, + exposedPorts: nil, + }, + expected: map[nat.Port][]nat.PortBinding{}, + }, + { + name: "config port map but not exposed", + arg: arg{ + configPortMap: map[nat.Port][]nat.PortBinding{ + "80/tcp": {{HostIP: "1", HostPort: "2"}}, + }, + parsedPortMap: nil, + exposedPorts: nil, + }, + expected: map[nat.Port][]nat.PortBinding{}, + }, + { + name: "parsed port map without config", + arg: arg{ + configPortMap: nil, + parsedPortMap: map[nat.Port][]nat.PortBinding{ + "80/tcp": {{HostIP: "", HostPort: ""}}, + }, + exposedPorts: nil, + }, + expected: map[nat.Port][]nat.PortBinding{ + "80/tcp": {{HostIP: "", HostPort: ""}}, + }, + }, + { + name: "parsed and configured but not exposed", + arg: arg{ + configPortMap: map[nat.Port][]nat.PortBinding{ + "80/tcp": {{HostIP: "1", HostPort: "2"}}, + }, + parsedPortMap: map[nat.Port][]nat.PortBinding{ + "80/tcp": {{HostIP: "", HostPort: ""}}, + }, + exposedPorts: nil, + }, + expected: map[nat.Port][]nat.PortBinding{ + "80/tcp": {{HostIP: "", HostPort: ""}}, + }, + }, + { + name: "merge both parsed and config", + arg: arg{ + configPortMap: map[nat.Port][]nat.PortBinding{ + "60/tcp": {{HostIP: "1", HostPort: "2"}}, + "70/tcp": {{HostIP: "1", HostPort: "2"}}, + "80/tcp": {{HostIP: "1", HostPort: "2"}}, + }, + parsedPortMap: map[nat.Port][]nat.PortBinding{ + "80/tcp": {{HostIP: "", HostPort: ""}}, + "90/tcp": {{HostIP: "", HostPort: ""}}, + }, + exposedPorts: []string{"70", "80"}, + }, + expected: map[nat.Port][]nat.PortBinding{ + "70/tcp": {{HostIP: "1", HostPort: "2"}}, + "80/tcp": {{HostIP: "1", HostPort: "2"}}, + "90/tcp": {{HostIP: "", HostPort: ""}}, + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + res := mergePortBindings(c.arg.configPortMap, c.arg.parsedPortMap, c.arg.exposedPorts) + assert.Equal(t, c.expected, res) + }) + } +} diff --git a/lifecycle_test.go b/lifecycle_test.go index ac8d8e1ee3..da912c7f26 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "bufio" @@ -16,29 +16,30 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) func TestPreCreateModifierHook(t *testing.T) { ctx := context.Background() - provider, err := NewDockerProvider() + provider, err := testcontainers.NewDockerProvider() require.NoError(t, err) defer provider.Close() t.Run("No exposed ports", func(t *testing.T) { // reqWithModifiers { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, // alpine image does expose port 80 ConfigModifier: func(config *container.Config) { config.Env = []string{"a=b"} }, - Mounts: ContainerMounts{ + Mounts: testcontainers.ContainerMounts{ { - Source: DockerVolumeMountSource{ + Source: testcontainers.DockerVolumeMountSource{ Name: "appdata", VolumeOptions: &mount.VolumeOptions{ - Labels: GenericLabels(), + Labels: testcontainers.GenericLabels(), }, }, Target: "/data", @@ -70,7 +71,8 @@ func TestPreCreateModifierHook(t *testing.T) { inputHostConfig := &container.HostConfig{} inputNetworkingConfig := &network.NetworkingConfig{} - err = provider.preCreateContainerHook(ctx, req, inputConfig, inputHostConfig, inputNetworkingConfig) + hooks := provider.DefaultDockerLifecycleHook(req, inputConfig, inputHostConfig, inputNetworkingConfig) + err = hooks.Creating(ctx)(req) require.NoError(t, err) // assertions @@ -94,7 +96,7 @@ func TestPreCreateModifierHook(t *testing.T) { Source: "appdata", Target: "/data", VolumeOptions: &mount.VolumeOptions{ - Labels: GenericLabels(), + Labels: testcontainers.GenericLabels(), }, }, }, @@ -128,7 +130,7 @@ func TestPreCreateModifierHook(t *testing.T) { }) t.Run("No exposed ports and network mode IsContainer", func(t *testing.T) { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, // alpine image does expose port 80 HostConfigModifier: func(hostConfig *container.HostConfig) { hostConfig.PortBindings = nat.PortMap{ @@ -150,7 +152,8 @@ func TestPreCreateModifierHook(t *testing.T) { inputHostConfig := &container.HostConfig{} inputNetworkingConfig := &network.NetworkingConfig{} - err = provider.preCreateContainerHook(ctx, req, inputConfig, inputHostConfig, inputNetworkingConfig) + hooks := provider.DefaultDockerLifecycleHook(req, inputConfig, inputHostConfig, inputNetworkingConfig) + err = hooks.Creating(ctx)(req) require.NoError(t, err) // assertions @@ -169,7 +172,7 @@ func TestPreCreateModifierHook(t *testing.T) { }) t.Run("Nil hostConfigModifier should apply default host config modifier", func(t *testing.T) { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, // alpine image does expose port 80 AutoRemove: true, CapAdd: []string{"addFoo", "addBar"}, @@ -191,7 +194,8 @@ func TestPreCreateModifierHook(t *testing.T) { inputHostConfig := &container.HostConfig{} inputNetworkingConfig := &network.NetworkingConfig{} - err = provider.preCreateContainerHook(ctx, req, inputConfig, inputHostConfig, inputNetworkingConfig) + hooks := provider.DefaultDockerLifecycleHook(req, inputConfig, inputHostConfig, inputNetworkingConfig) + err = hooks.Creating(ctx)(req) require.NoError(t, err) // assertions @@ -206,7 +210,7 @@ func TestPreCreateModifierHook(t *testing.T) { t.Run("Request contains more than one network including aliases", func(t *testing.T) { networkName := "foo" - net, err := provider.CreateNetwork(ctx, NetworkRequest{ + net, err := provider.CreateNetwork(ctx, testcontainers.NetworkRequest{ Name: networkName, }) require.NoError(t, err) @@ -217,12 +221,12 @@ func TestPreCreateModifierHook(t *testing.T) { } }() - dockerNetwork, err := provider.GetNetwork(ctx, NetworkRequest{ + dockerNetwork, err := provider.GetNetwork(ctx, testcontainers.NetworkRequest{ Name: networkName, }) require.NoError(t, err) - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, // alpine image does expose port 80 Networks: []string{networkName, "bar"}, NetworkAliases: map[string][]string{ @@ -237,7 +241,8 @@ func TestPreCreateModifierHook(t *testing.T) { inputHostConfig := &container.HostConfig{} inputNetworkingConfig := &network.NetworkingConfig{} - err = provider.preCreateContainerHook(ctx, req, inputConfig, inputHostConfig, inputNetworkingConfig) + hooks := provider.DefaultDockerLifecycleHook(req, inputConfig, inputHostConfig, inputNetworkingConfig) + err = hooks.Creating(ctx)(req) require.NoError(t, err) // assertions @@ -258,7 +263,7 @@ func TestPreCreateModifierHook(t *testing.T) { t.Run("Request contains more than one network without aliases", func(t *testing.T) { networkName := "foo" - net, err := provider.CreateNetwork(ctx, NetworkRequest{ + net, err := provider.CreateNetwork(ctx, testcontainers.NetworkRequest{ Name: networkName, }) require.NoError(t, err) @@ -269,12 +274,12 @@ func TestPreCreateModifierHook(t *testing.T) { } }() - dockerNetwork, err := provider.GetNetwork(ctx, NetworkRequest{ + dockerNetwork, err := provider.GetNetwork(ctx, testcontainers.NetworkRequest{ Name: networkName, }) require.NoError(t, err) - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, // alpine image does expose port 80 Networks: []string{networkName, "bar"}, } @@ -286,7 +291,8 @@ func TestPreCreateModifierHook(t *testing.T) { inputHostConfig := &container.HostConfig{} inputNetworkingConfig := &network.NetworkingConfig{} - err = provider.preCreateContainerHook(ctx, req, inputConfig, inputHostConfig, inputNetworkingConfig) + hooks := provider.DefaultDockerLifecycleHook(req, inputConfig, inputHostConfig, inputNetworkingConfig) + err = hooks.Creating(ctx)(req) require.NoError(t, err) // assertions @@ -305,7 +311,7 @@ func TestPreCreateModifierHook(t *testing.T) { }) t.Run("Request contains exposed port modifiers", func(t *testing.T) { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, // alpine image does expose port 80 HostConfigModifier: func(hostConfig *container.HostConfig) { hostConfig.PortBindings = nat.PortMap{ @@ -327,7 +333,8 @@ func TestPreCreateModifierHook(t *testing.T) { inputHostConfig := &container.HostConfig{} inputNetworkingConfig := &network.NetworkingConfig{} - err = provider.preCreateContainerHook(ctx, req, inputConfig, inputHostConfig, inputNetworkingConfig) + hooks := provider.DefaultDockerLifecycleHook(req, inputConfig, inputHostConfig, inputNetworkingConfig) + err = hooks.Creating(ctx)(req) require.NoError(t, err) // assertions @@ -336,95 +343,6 @@ func TestPreCreateModifierHook(t *testing.T) { }) } -func TestMergePortBindings(t *testing.T) { - type arg struct { - configPortMap nat.PortMap - parsedPortMap nat.PortMap - exposedPorts []string - } - cases := []struct { - name string - arg arg - expected nat.PortMap - }{ - { - name: "empty ports", - arg: arg{ - configPortMap: nil, - parsedPortMap: nil, - exposedPorts: nil, - }, - expected: map[nat.Port][]nat.PortBinding{}, - }, - { - name: "config port map but not exposed", - arg: arg{ - configPortMap: map[nat.Port][]nat.PortBinding{ - "80/tcp": {{HostIP: "1", HostPort: "2"}}, - }, - parsedPortMap: nil, - exposedPorts: nil, - }, - expected: map[nat.Port][]nat.PortBinding{}, - }, - { - name: "parsed port map without config", - arg: arg{ - configPortMap: nil, - parsedPortMap: map[nat.Port][]nat.PortBinding{ - "80/tcp": {{HostIP: "", HostPort: ""}}, - }, - exposedPorts: nil, - }, - expected: map[nat.Port][]nat.PortBinding{ - "80/tcp": {{HostIP: "", HostPort: ""}}, - }, - }, - { - name: "parsed and configured but not exposed", - arg: arg{ - configPortMap: map[nat.Port][]nat.PortBinding{ - "80/tcp": {{HostIP: "1", HostPort: "2"}}, - }, - parsedPortMap: map[nat.Port][]nat.PortBinding{ - "80/tcp": {{HostIP: "", HostPort: ""}}, - }, - exposedPorts: nil, - }, - expected: map[nat.Port][]nat.PortBinding{ - "80/tcp": {{HostIP: "", HostPort: ""}}, - }, - }, - { - name: "merge both parsed and config", - arg: arg{ - configPortMap: map[nat.Port][]nat.PortBinding{ - "60/tcp": {{HostIP: "1", HostPort: "2"}}, - "70/tcp": {{HostIP: "1", HostPort: "2"}}, - "80/tcp": {{HostIP: "1", HostPort: "2"}}, - }, - parsedPortMap: map[nat.Port][]nat.PortBinding{ - "80/tcp": {{HostIP: "", HostPort: ""}}, - "90/tcp": {{HostIP: "", HostPort: ""}}, - }, - exposedPorts: []string{"70", "80"}, - }, - expected: map[nat.Port][]nat.PortBinding{ - "70/tcp": {{HostIP: "1", HostPort: "2"}}, - "80/tcp": {{HostIP: "1", HostPort: "2"}}, - "90/tcp": {{HostIP: "", HostPort: ""}}, - }, - }, - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - res := mergePortBindings(c.arg.configPortMap, c.arg.parsedPortMap, c.arg.exposedPorts) - assert.Equal(t, c.expected, res) - }) - } -} - func TestLifecycleHooks(t *testing.T) { tests := []struct { name string @@ -445,86 +363,86 @@ func TestLifecycleHooks(t *testing.T) { prints := []string{} ctx := context.Background() // reqWithLifecycleHooks { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, - LifecycleHooks: []ContainerLifecycleHooks{ + LifecycleHooks: []testcontainers.ContainerLifecycleHooks{ { - PreCreates: []ContainerRequestHook{ - func(ctx context.Context, req ContainerRequest) error { + PreCreates: []testcontainers.ContainerRequestHook{ + func(ctx context.Context, req testcontainers.ContainerRequest) error { prints = append(prints, fmt.Sprintf("pre-create hook 1: %#v", req)) return nil }, - func(ctx context.Context, req ContainerRequest) error { + func(ctx context.Context, req testcontainers.ContainerRequest) error { prints = append(prints, fmt.Sprintf("pre-create hook 2: %#v", req)) return nil }, }, - PostCreates: []ContainerHook{ - func(ctx context.Context, c Container) error { + PostCreates: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-create hook 1: %#v", c)) return nil }, - func(ctx context.Context, c Container) error { + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-create hook 2: %#v", c)) return nil }, }, - PreStarts: []ContainerHook{ - func(ctx context.Context, c Container) error { + PreStarts: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("pre-start hook 1: %#v", c)) return nil }, - func(ctx context.Context, c Container) error { + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("pre-start hook 2: %#v", c)) return nil }, }, - PostStarts: []ContainerHook{ - func(ctx context.Context, c Container) error { + PostStarts: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-start hook 1: %#v", c)) return nil }, - func(ctx context.Context, c Container) error { + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-start hook 2: %#v", c)) return nil }, }, - PreStops: []ContainerHook{ - func(ctx context.Context, c Container) error { + PreStops: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("pre-stop hook 1: %#v", c)) return nil }, - func(ctx context.Context, c Container) error { + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("pre-stop hook 2: %#v", c)) return nil }, }, - PostStops: []ContainerHook{ - func(ctx context.Context, c Container) error { + PostStops: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-stop hook 1: %#v", c)) return nil }, - func(ctx context.Context, c Container) error { + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-stop hook 2: %#v", c)) return nil }, }, - PreTerminates: []ContainerHook{ - func(ctx context.Context, c Container) error { + PreTerminates: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("pre-terminate hook 1: %#v", c)) return nil }, - func(ctx context.Context, c Container) error { + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("pre-terminate hook 2: %#v", c)) return nil }, }, - PostTerminates: []ContainerHook{ - func(ctx context.Context, c Container) error { + PostTerminates: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-terminate hook 1: %#v", c)) return nil }, - func(ctx context.Context, c Container) error { + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-terminate hook 2: %#v", c)) return nil }, @@ -538,7 +456,7 @@ func TestLifecycleHooks(t *testing.T) { req.Name = "reuse-container" } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Reuse: tt.reuse, Started: true, @@ -575,15 +493,15 @@ func TestLifecycleHooks_WithDefaultLogger(t *testing.T) { // reqWithDefaultLogginHook { dl := inMemoryLogger{} - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, - LifecycleHooks: []ContainerLifecycleHooks{ - DefaultLoggingHook(&dl), + LifecycleHooks: []testcontainers.ContainerLifecycleHooks{ + testcontainers.DefaultLoggingHook(&dl), }, } // } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -608,15 +526,15 @@ func TestLifecycleHooks_WithMultipleHooks(t *testing.T) { dl := inMemoryLogger{} - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: nginxAlpineImage, - LifecycleHooks: []ContainerLifecycleHooks{ - DefaultLoggingHook(&dl), - DefaultLoggingHook(&dl), + LifecycleHooks: []testcontainers.ContainerLifecycleHooks{ + testcontainers.DefaultLoggingHook(&dl), + testcontainers.DefaultLoggingHook(&dl), }, } - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -646,13 +564,13 @@ func (l *linesTestLogger) Printf(format string, args ...interface{}) { func TestPrintContainerLogsOnError(t *testing.T) { ctx := context.Background() - client, err := NewDockerClientWithOpts(ctx) + client, err := testcontainers.NewDockerClientWithOpts(ctx) if err != nil { t.Fatal(err) } defer client.Close() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "docker.io/alpine", Cmd: []string{"echo", "-n", "I am expecting this"}, WaitingFor: wait.ForLog("I was expecting that").WithStartupTimeout(5 * time.Second), @@ -662,7 +580,7 @@ func TestPrintContainerLogsOnError(t *testing.T) { data: []string{}, } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Logger: &arrayOfLinesLogger, @@ -705,7 +623,7 @@ func TestPrintContainerLogsOnError(t *testing.T) { } } -func lifecycleHooksIsHonouredFn(t *testing.T, ctx context.Context, container Container, prints []string) { +func lifecycleHooksIsHonouredFn(t *testing.T, ctx context.Context, container testcontainers.Container, prints []string) { require.Len(t, prints, 20) assert.True(t, strings.HasPrefix(prints[0], "pre-create hook 1: ")) diff --git a/logconsumer_test.go b/logconsumer_test.go index 3416c73f79..da79b46705 100644 --- a/logconsumer_test.go +++ b/logconsumer_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/internal/config" "github.com/testcontainers/testcontainers-go/wait" ) @@ -31,7 +32,7 @@ type TestLogConsumer struct { Accepted chan string } -func (g *TestLogConsumer) Accept(l Log) { +func (g *TestLogConsumer) Accept(l testcontainers.Log) { s := string(l.Content) if s == fmt.Sprintf("echo %s\n", lastMessage) { g.Done <- true @@ -61,24 +62,24 @@ func Test_LogConsumerGetsCalled(t *testing.T) { Accepted: devNullAcceptorChan(), } - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata/", Dockerfile: "echoserver.Dockerfile", }, ExposedPorts: []string{"8080/tcp"}, WaitingFor: wait.ForLog("ready"), - LogConsumerCfg: &LogConsumerConfig{ - Consumers: []LogConsumer{&g}, + LogConsumerCfg: &testcontainers.LogConsumerConfig{ + Consumers: []testcontainers.LogConsumer{&g}, }, } - gReq := GenericContainerRequest{ + gReq := testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, gReq) + c, err := testcontainers.GenericContainer(ctx, gReq) require.NoError(t, err) ep, err := c.Endpoint(ctx, "http") @@ -109,7 +110,7 @@ type TestLogTypeConsumer struct { Ack chan bool } -func (t *TestLogTypeConsumer) Accept(l Log) { +func (t *TestLogTypeConsumer) Accept(l testcontainers.Log) { if string(l.Content) == fmt.Sprintf("echo %s\n", lastMessage) { t.Ack <- true return @@ -126,24 +127,24 @@ func Test_ShouldRecognizeLogTypes(t *testing.T) { Ack: make(chan bool), } - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata/", Dockerfile: "echoserver.Dockerfile", }, ExposedPorts: []string{"8080/tcp"}, WaitingFor: wait.ForLog("ready"), - LogConsumerCfg: &LogConsumerConfig{ - Consumers: []LogConsumer{&g}, + LogConsumerCfg: &testcontainers.LogConsumerConfig{ + Consumers: []testcontainers.LogConsumer{&g}, }, } - gReq := GenericContainerRequest{ + gReq := testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, gReq) + c, err := testcontainers.GenericContainer(ctx, gReq) require.NoError(t, err) terminateContainerOnEnd(t, ctx, c) @@ -162,8 +163,8 @@ func Test_ShouldRecognizeLogTypes(t *testing.T) { <-g.Ack assert.Equal(t, map[string]string{ - StdoutLog: "echo this-is-stdout\n", - StderrLog: "echo this-is-stderr\n", + testcontainers.StdoutLog: "echo this-is-stdout\n", + testcontainers.StderrLog: "echo this-is-stderr\n", }, g.LogTypes) } @@ -181,24 +182,24 @@ func Test_MultipleLogConsumers(t *testing.T) { Accepted: devNullAcceptorChan(), } - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata/", Dockerfile: "echoserver.Dockerfile", }, ExposedPorts: []string{"8080/tcp"}, WaitingFor: wait.ForLog("ready"), - LogConsumerCfg: &LogConsumerConfig{ - Consumers: []LogConsumer{&first, &second}, + LogConsumerCfg: &testcontainers.LogConsumerConfig{ + Consumers: []testcontainers.LogConsumer{&first, &second}, }, } - gReq := GenericContainerRequest{ + gReq := testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, gReq) + c, err := testcontainers.GenericContainer(ctx, gReq) require.NoError(t, err) ep, err := c.Endpoint(ctx, "http") @@ -227,7 +228,7 @@ func TestContainerLogWithErrClosed(t *testing.T) { config.Reset() }) - if providerType == ProviderPodman { + if providerType == testcontainers.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 @@ -235,9 +236,9 @@ func TestContainerLogWithErrClosed(t *testing.T) { // closed to test behaviour in connection-closed situations. ctx := context.Background() - dind, err := GenericContainer(ctx, GenericContainerRequest{ + dind, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ Started: true, - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/docker:dind", ExposedPorts: []string{"2375/tcp"}, Env: map[string]string{"DOCKER_TLS_CERTDIR": ""}, @@ -271,22 +272,10 @@ func TestContainerLogWithErrClosed(t *testing.T) { } opts := []client.Opt{client.WithHost(remoteDocker), client.WithAPIVersionNegotiation()} - - client, err := NewDockerClientWithOpts(ctx, opts...) + provider, err := testcontainers.NewDockerProviderWithClientOpts(opts, testcontainers.WithLogger(testcontainers.TestLogger(t))) if err != nil { t.Fatal(err) } - defer client.Close() - - provider := &DockerProvider{ - client: client, - config: ReadConfig(), - DockerProviderOptions: &DockerProviderOptions{ - GenericProviderOptions: &GenericProviderOptions{ - Logger: TestLogger(t), - }, - }, - } consumer := TestLogConsumer{ Msgs: []string{}, @@ -294,11 +283,11 @@ func TestContainerLogWithErrClosed(t *testing.T) { Accepted: devNullAcceptorChan(), } - nginx, err := provider.CreateContainer(ctx, ContainerRequest{ + nginx, err := provider.CreateContainer(ctx, testcontainers.ContainerRequest{ Image: "nginx", ExposedPorts: []string{"80/tcp"}, - LogConsumerCfg: &LogConsumerConfig{ - Consumers: []LogConsumer{&consumer}, + LogConsumerCfg: &testcontainers.LogConsumerConfig{ + Consumers: []testcontainers.LogConsumer{&consumer}, }, }) if err != nil { @@ -360,12 +349,12 @@ func TestContainerLogWithErrClosed(t *testing.T) { func TestContainerLogsShouldBeWithoutStreamHeader(t *testing.T) { ctx := context.Background() - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "alpine:latest", Cmd: []string{"sh", "-c", "id -u"}, WaitingFor: wait.ForExit(), } - container, err := GenericContainer(ctx, GenericContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -394,26 +383,26 @@ func TestContainerLogsEnableAtStart(t *testing.T) { } // logConsumersAtRequest { - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata/", Dockerfile: "echoserver.Dockerfile", }, ExposedPorts: []string{"8080/tcp"}, WaitingFor: wait.ForLog("ready"), - LogConsumerCfg: &LogConsumerConfig{ - Opts: []LogProductionOption{WithLogProductionTimeout(10 * time.Second)}, - Consumers: []LogConsumer{&g}, + LogConsumerCfg: &testcontainers.LogConsumerConfig{ + Opts: []testcontainers.LogProductionOption{testcontainers.WithLogProductionTimeout(10 * time.Second)}, + Consumers: []testcontainers.LogConsumer{&g}, }, } // } - gReq := GenericContainerRequest{ + gReq := testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, gReq) + c, err := testcontainers.GenericContainer(ctx, gReq) require.NoError(t, err) ep, err := c.Endpoint(ctx, "http") @@ -447,25 +436,25 @@ func Test_StartLogProductionStillStartsWithTooLowTimeout(t *testing.T) { Accepted: devNullAcceptorChan(), } - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata/", Dockerfile: "echoserver.Dockerfile", }, ExposedPorts: []string{"8080/tcp"}, WaitingFor: wait.ForLog("ready"), - LogConsumerCfg: &LogConsumerConfig{ - Opts: []LogProductionOption{WithLogProductionTimeout(4 * time.Second)}, - Consumers: []LogConsumer{&g}, + LogConsumerCfg: &testcontainers.LogConsumerConfig{ + Opts: []testcontainers.LogProductionOption{testcontainers.WithLogProductionTimeout(4 * time.Second)}, + Consumers: []testcontainers.LogConsumer{&g}, }, } - gReq := GenericContainerRequest{ + gReq := testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, gReq) + c, err := testcontainers.GenericContainer(ctx, gReq) require.NoError(t, err) terminateContainerOnEnd(t, ctx, c) } @@ -479,32 +468,29 @@ func Test_StartLogProductionStillStartsWithTooHighTimeout(t *testing.T) { Accepted: devNullAcceptorChan(), } - req := ContainerRequest{ - FromDockerfile: FromDockerfile{ + req := testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ Context: "./testdata/", Dockerfile: "echoserver.Dockerfile", }, ExposedPorts: []string{"8080/tcp"}, WaitingFor: wait.ForLog("ready"), - LogConsumerCfg: &LogConsumerConfig{ - Opts: []LogProductionOption{WithLogProductionTimeout(61 * time.Second)}, - Consumers: []LogConsumer{&g}, + LogConsumerCfg: &testcontainers.LogConsumerConfig{ + Opts: []testcontainers.LogProductionOption{testcontainers.WithLogProductionTimeout(61 * time.Second)}, + Consumers: []testcontainers.LogConsumer{&g}, }, } - gReq := GenericContainerRequest{ + gReq := testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, } - c, err := GenericContainer(ctx, gReq) + c, err := testcontainers.GenericContainer(ctx, gReq) require.NoError(t, err) require.NotNil(t, c) // because the log production timeout is too high, the container should have already been terminated - // so no need to terminate it again with "terminateContainerOnEnd(t, ctx, c)" - dc := c.(*DockerContainer) - require.NoError(t, dc.stopLogProduction()) - + // so the logs should also automatically be terminated thanks to the hooks. Terminate it again to be safe. terminateContainerOnEnd(t, ctx, c) } diff --git a/mounts_test.go b/mounts_test.go index a89e6355c0..50c46e20af 100644 --- a/mounts_test.go +++ b/mounts_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -7,48 +7,50 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/testcontainers/testcontainers-go" ) func TestVolumeMount(t *testing.T) { t.Parallel() type args struct { volumeName string - mountTarget ContainerMountTarget + mountTarget testcontainers.ContainerMountTarget } tests := []struct { name string args args - want ContainerMount + want testcontainers.ContainerMount }{ { name: "sample-data:/data", args: args{volumeName: "sample-data", mountTarget: "/data"}, - want: ContainerMount{Source: GenericVolumeMountSource{Name: "sample-data"}, Target: "/data"}, + want: testcontainers.ContainerMount{Source: testcontainers.GenericVolumeMountSource{Name: "sample-data"}, Target: "/data"}, }, { name: "web:/var/nginx/html", args: args{volumeName: "web", mountTarget: "/var/nginx/html"}, - want: ContainerMount{Source: GenericVolumeMountSource{Name: "web"}, Target: "/var/nginx/html"}, + want: testcontainers.ContainerMount{Source: testcontainers.GenericVolumeMountSource{Name: "web"}, Target: "/var/nginx/html"}, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - assert.Equalf(t, tt.want, VolumeMount(tt.args.volumeName, tt.args.mountTarget), "VolumeMount(%v, %v)", tt.args.volumeName, tt.args.mountTarget) + assert.Equalf(t, tt.want, testcontainers.VolumeMount(tt.args.volumeName, tt.args.mountTarget), "VolumeMount(%v, %v)", tt.args.volumeName, tt.args.mountTarget) }) } } func TestContainerMounts_PrepareMounts(t *testing.T) { volumeOptions := &mount.VolumeOptions{ - Labels: GenericLabels(), + Labels: testcontainers.GenericLabels(), } t.Parallel() tests := []struct { name string - mounts ContainerMounts + mounts testcontainers.ContainerMounts want []mount.Mount }{ { @@ -58,7 +60,7 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { }, { name: "Single volume mount", - mounts: ContainerMounts{{Source: GenericVolumeMountSource{Name: "app-data"}, Target: "/data"}}, + mounts: testcontainers.ContainerMounts{{Source: testcontainers.GenericVolumeMountSource{Name: "app-data"}, Target: "/data"}}, want: []mount.Mount{ { Type: mount.TypeVolume, @@ -70,7 +72,7 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { }, { name: "Single volume mount - read-only", - mounts: ContainerMounts{{Source: GenericVolumeMountSource{Name: "app-data"}, Target: "/data", ReadOnly: true}}, + mounts: testcontainers.ContainerMounts{{Source: testcontainers.GenericVolumeMountSource{Name: "app-data"}, Target: "/data", ReadOnly: true}}, want: []mount.Mount{ { Type: mount.TypeVolume, @@ -83,9 +85,9 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { }, { name: "Single volume mount - with options", - mounts: ContainerMounts{ + mounts: testcontainers.ContainerMounts{ { - Source: DockerVolumeMountSource{ + Source: testcontainers.DockerVolumeMountSource{ Name: "app-data", VolumeOptions: &mount.VolumeOptions{ NoCopy: true, @@ -114,7 +116,7 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { { name: "Single tmpfs mount", - mounts: ContainerMounts{{Source: GenericTmpfsMountSource{}, Target: "/data"}}, + mounts: testcontainers.ContainerMounts{{Source: testcontainers.GenericTmpfsMountSource{}, Target: "/data"}}, want: []mount.Mount{ { Type: mount.TypeTmpfs, @@ -125,7 +127,7 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { }, { name: "Single volume mount - read-only", - mounts: ContainerMounts{{Source: GenericTmpfsMountSource{}, Target: "/data", ReadOnly: true}}, + mounts: testcontainers.ContainerMounts{{Source: testcontainers.GenericTmpfsMountSource{}, Target: "/data", ReadOnly: true}}, want: []mount.Mount{ { Type: mount.TypeTmpfs, @@ -137,9 +139,9 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { }, { name: "Single volume mount - with options", - mounts: ContainerMounts{ + mounts: testcontainers.ContainerMounts{ { - Source: DockerTmpfsMountSource{ + Source: testcontainers.DockerTmpfsMountSource{ TmpfsOptions: &mount.TmpfsOptions{ SizeBytes: 50 * 1024 * 1024, Mode: 0o644, @@ -165,18 +167,18 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - assert.Equalf(t, tt.want, mapToDockerMounts(tt.mounts), "PrepareMounts()") + assert.Equalf(t, tt.want, tt.mounts.PrepareMounts(), "PrepareMounts()") }) } } func TestCreateContainerWithVolume(t *testing.T) { // volumeMounts { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "alpine", - Mounts: ContainerMounts{ + Mounts: testcontainers.ContainerMounts{ { - Source: GenericVolumeMountSource{ + Source: testcontainers.GenericVolumeMountSource{ Name: "test-volume", }, Target: "/data", @@ -186,7 +188,7 @@ func TestCreateContainerWithVolume(t *testing.T) { // } ctx := context.Background() - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -194,7 +196,7 @@ func TestCreateContainerWithVolume(t *testing.T) { terminateContainerOnEnd(t, ctx, c) // Check if volume is created - client, err := NewDockerClientWithOpts(ctx) + client, err := testcontainers.NewDockerClientWithOpts(ctx) require.NoError(t, err) defer client.Close() @@ -204,11 +206,11 @@ func TestCreateContainerWithVolume(t *testing.T) { } func TestMountsReceiveRyukLabels(t *testing.T) { - req := ContainerRequest{ + req := testcontainers.ContainerRequest{ Image: "alpine", - Mounts: ContainerMounts{ + Mounts: testcontainers.ContainerMounts{ { - Source: GenericVolumeMountSource{ + Source: testcontainers.GenericVolumeMountSource{ Name: "app-data", }, Target: "/data", @@ -217,7 +219,7 @@ func TestMountsReceiveRyukLabels(t *testing.T) { } ctx := context.Background() - c, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) @@ -225,11 +227,11 @@ func TestMountsReceiveRyukLabels(t *testing.T) { terminateContainerOnEnd(t, ctx, c) // Check if volume is created with the expected labels - client, err := NewDockerClientWithOpts(ctx) + client, err := testcontainers.NewDockerClientWithOpts(ctx) require.NoError(t, err) defer client.Close() volume, err := client.VolumeInspect(ctx, "app-data") require.NoError(t, err) - assert.Equal(t, GenericLabels(), volume.Labels) + assert.Equal(t, testcontainers.GenericLabels(), volume.Labels) } diff --git a/parallel_test.go b/parallel_test.go index 122f59a4f7..4e7938140c 100644 --- a/parallel_test.go +++ b/parallel_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -9,21 +9,22 @@ import ( "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) func TestParallelContainers(t *testing.T) { tests := []struct { name string - reqs ParallelContainerRequest + reqs testcontainers.ParallelContainerRequest resLen int expErrors int }{ { name: "running two containers (one error)", - reqs: ParallelContainerRequest{ + reqs: testcontainers.ParallelContainerRequest{ { - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "nginx", ExposedPorts: []string{ "10080/tcp", @@ -32,7 +33,7 @@ func TestParallelContainers(t *testing.T) { Started: true, }, { - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "bad bad bad", ExposedPorts: []string{ "10081/tcp", @@ -46,9 +47,9 @@ func TestParallelContainers(t *testing.T) { }, { name: "running two containers (all errors)", - reqs: ParallelContainerRequest{ + reqs: testcontainers.ParallelContainerRequest{ { - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "bad bad bad", ExposedPorts: []string{ "10081/tcp", @@ -57,7 +58,7 @@ func TestParallelContainers(t *testing.T) { Started: true, }, { - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "bad bad bad", ExposedPorts: []string{ "10081/tcp", @@ -71,9 +72,9 @@ func TestParallelContainers(t *testing.T) { }, { name: "running two containers (success)", - reqs: ParallelContainerRequest{ + reqs: testcontainers.ParallelContainerRequest{ { - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "nginx", ExposedPorts: []string{ "10080/tcp", @@ -82,7 +83,7 @@ func TestParallelContainers(t *testing.T) { Started: true, }, { - ContainerRequest: ContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "nginx", ExposedPorts: []string{ "10081/tcp", @@ -98,10 +99,10 @@ func TestParallelContainers(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - res, err := ParallelContainers(context.Background(), tc.reqs, ParallelContainersOptions{}) + res, err := testcontainers.ParallelContainers(context.Background(), tc.reqs, testcontainers.ParallelContainersOptions{}) if err != nil { require.NotZero(t, tc.expErrors) - var e ParallelContainersError + var e testcontainers.ParallelContainersError errors.As(err, &e) if len(e.Errors) != tc.expErrors { t.Fatalf("expected errors: %d, got: %d\n", tc.expErrors, len(e.Errors)) @@ -130,8 +131,8 @@ func TestParallelContainersWithReuse(t *testing.T) { natPort := fmt.Sprintf("%d/tcp", postgresPort) - req := GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + req := testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "postgis/postgis", Name: "test-postgres", ExposedPorts: []string{natPort}, @@ -148,7 +149,7 @@ func TestParallelContainersWithReuse(t *testing.T) { Reuse: true, } - parallelRequest := ParallelContainerRequest{ + parallelRequest := testcontainers.ParallelContainerRequest{ req, req, req, @@ -156,9 +157,9 @@ func TestParallelContainersWithReuse(t *testing.T) { ctx := context.Background() - res, err := ParallelContainers(ctx, parallelRequest, ParallelContainersOptions{}) + res, err := testcontainers.ParallelContainers(ctx, parallelRequest, testcontainers.ParallelContainersOptions{}) if err != nil { - var e ParallelContainersError + var e testcontainers.ParallelContainersError errors.As(err, &e) t.Fatalf("expected errors: %d, got: %d\n", 0, len(e.Errors)) } diff --git a/provider.go b/provider.go index 1be1ebe20a..7e10be4808 100644 --- a/provider.go +++ b/provider.go @@ -7,6 +7,8 @@ import ( "os" "strings" + "github.com/docker/docker/client" + "github.com/testcontainers/testcontainers-go/internal/core" ) @@ -38,7 +40,7 @@ type ( // DockerProviderOptions defines options applicable to DockerProvider DockerProviderOptions struct { - defaultBridgeNetworkName string + DefaultBridgeNetworkName string *GenericProviderOptions } @@ -74,7 +76,7 @@ func Generic2DockerOptions(opts ...GenericProviderOption) []DockerProviderOption func WithDefaultBridgeNetwork(bridgeNetworkName string) DockerProviderOption { return DockerProviderOptionFunc(func(opts *DockerProviderOptions) { - opts.defaultBridgeNetworkName = bridgeNetworkName + opts.DefaultBridgeNetworkName = bridgeNetworkName }) } @@ -128,6 +130,12 @@ func (t ProviderType) GetProvider(opts ...GenericProviderOption) (GenericProvide // NewDockerProvider creates a Docker provider with the EnvClient func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error) { + return NewDockerProviderWithClientOpts([]client.Opt{}, provOpts...) +} + +// NewDockerProviderWithClientOpts creates a Docker provider with the EnvClient using the provided client opts +// for the docker client. +func NewDockerProviderWithClientOpts(clientOpts []client.Opt, provOpts ...DockerProviderOption) (*DockerProvider, error) { o := &DockerProviderOptions{ GenericProviderOptions: &GenericProviderOptions{ Logger: Logger, @@ -139,7 +147,7 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error } ctx := context.Background() - c, err := NewDockerClientWithOpts(ctx) + c, err := NewDockerClientWithOpts(ctx, clientOpts...) if err != nil { return nil, err } diff --git a/provider_test.go b/provider_test.go index 2448d84c07..39b67f38a9 100644 --- a/provider_test.go +++ b/provider_test.go @@ -1,9 +1,10 @@ -package testcontainers +package testcontainers_test import ( "context" "testing" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/internal/core" ) @@ -13,52 +14,52 @@ func TestProviderTypeGetProviderAutodetect(t *testing.T) { tests := []struct { name string - tr ProviderType + tr testcontainers.ProviderType DockerHost string want string wantErr bool }{ { name: "default provider without podman.socket", - tr: ProviderDefault, + tr: testcontainers.ProviderDefault, DockerHost: dockerHost, - want: Bridge, + want: testcontainers.Bridge, }, { name: "default provider with podman.socket", - tr: ProviderDefault, + tr: testcontainers.ProviderDefault, DockerHost: podmanSocket, - want: Podman, + want: testcontainers.Podman, }, { name: "docker provider without podman.socket", - tr: ProviderDocker, + tr: testcontainers.ProviderDocker, DockerHost: dockerHost, - want: Bridge, + want: testcontainers.Bridge, }, { // Explicitly setting Docker provider should not be overridden by auto-detect name: "docker provider with podman.socket", - tr: ProviderDocker, + tr: testcontainers.ProviderDocker, DockerHost: podmanSocket, - want: Bridge, + want: testcontainers.Bridge, }, { name: "Podman provider without podman.socket", - tr: ProviderPodman, + tr: testcontainers.ProviderPodman, DockerHost: dockerHost, - want: Podman, + want: testcontainers.Podman, }, { name: "Podman provider with podman.socket", - tr: ProviderPodman, + tr: testcontainers.ProviderPodman, DockerHost: podmanSocket, - want: Podman, + want: testcontainers.Podman, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.tr == ProviderPodman && core.IsWindows() { + if tt.tr == testcontainers.ProviderPodman && core.IsWindows() { t.Skip("Podman provider is not implemented for Windows") } @@ -69,12 +70,12 @@ func TestProviderTypeGetProviderAutodetect(t *testing.T) { t.Errorf("ProviderType.GetProvider() error = %v, wantErr %v", err, tt.wantErr) return } - provider, ok := got.(*DockerProvider) + provider, ok := got.(*testcontainers.DockerProvider) if !ok { - t.Fatalf("ProviderType.GetProvider() = %T, want %T", got, &DockerProvider{}) + t.Fatalf("ProviderType.GetProvider() = %T, want %T", got, &testcontainers.DockerProvider{}) } - if provider.defaultBridgeNetworkName != tt.want { - t.Errorf("ProviderType.GetProvider() = %v, want %v", provider.defaultBridgeNetworkName, tt.want) + if provider.DefaultBridgeNetworkName != tt.want { + t.Errorf("ProviderType.GetProvider() = %v, want %v", provider.DefaultBridgeNetworkName, tt.want) } }) } diff --git a/reaper_test.go b/reaper_test.go index d0b6f4fc7c..53e855b25f 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -1,9 +1,12 @@ +// This test is testing very internal logic that should not be exported away from this package. We'll +// leave it in the main testcontainers package. Do not use for user facing examples. package testcontainers import ( "context" "errors" "os" + "strings" "sync" "testing" "time" @@ -19,6 +22,30 @@ import ( "github.com/testcontainers/testcontainers-go/wait" ) +const ( + nginxAlpineImage = "docker.io/nginx:alpine" + nginxDefaultPort = "80/tcp" +) + +var providerType = ProviderDocker + +func init() { + if strings.Contains(os.Getenv("DOCKER_HOST"), "podman.sock") { + providerType = ProviderPodman + } +} + +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)) + }) +} + // testSessionID the tests need to create a reaper in a different session, so that it does not interfere with other tests const testSessionID = "this-is-a-different-session-id" diff --git a/testcontainers_test.go b/testcontainers_test.go index fe5af71e89..3e318f12d4 100644 --- a/testcontainers_test.go +++ b/testcontainers_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "fmt" @@ -6,19 +6,21 @@ import ( "os/exec" "regexp" "testing" + + "github.com/testcontainers/testcontainers-go" ) func TestSessionID(t *testing.T) { t.Run("SessionID() returns a non-empty string", func(t *testing.T) { - sessionID := SessionID() + sessionID := testcontainers.SessionID() if sessionID == "" { t.Error("SessionID() returned an empty string") } }) t.Run("Multiple calls to SessionID() return the same value", func(t *testing.T) { - sessionID1 := SessionID() - sessionID2 := SessionID() + sessionID1 := testcontainers.SessionID() + sessionID2 := testcontainers.SessionID() if sessionID1 != sessionID2 { t.Errorf("SessionID() returned different values: %s != %s", sessionID1, sessionID2) } @@ -30,12 +32,12 @@ func TestSessionID(t *testing.T) { done := make(chan bool) go func() { - sessionID1 = SessionID() + sessionID1 = testcontainers.SessionID() done <- true }() go func() { - sessionID2 = SessionID() + sessionID2 = testcontainers.SessionID() done <- true }() @@ -81,5 +83,5 @@ func TestSessionIDHelper(t *testing.T) { t.Skip("Not a real test, used as a test helper") } - fmt.Printf(">>>%s<<<\n", SessionID()) + fmt.Printf(">>>%s<<<\n", testcontainers.SessionID()) } diff --git a/testing_test.go b/testing_test.go index 56817d655a..de7d4e47d6 100644 --- a/testing_test.go +++ b/testing_test.go @@ -1,7 +1,11 @@ -package testcontainers +package testcontainers_test -import "testing" +import ( + "testing" + + "github.com/testcontainers/testcontainers-go" +) func ExampleSkipIfProviderIsNotHealthy() { - SkipIfProviderIsNotHealthy(&testing.T{}) + testcontainers.SkipIfProviderIsNotHealthy(&testing.T{}) }