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 33a73cae73..6d59ac327b 100644 --- a/docker.go +++ b/docker.go @@ -922,7 +922,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 { @@ -1076,13 +1076,13 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque // default hooks include logger hook and pre-create hook defaultHooks := []ContainerLifecycleHooks{ DefaultLoggingHook(p.Logger), - defaultPreCreateHook(ctx, p, req, dockerInput, hostConfig, networkingConfig), - defaultCopyFileToContainerHook(req.Files), - defaultLogConsumersHook(req.LogConsumerCfg), - defaultReadinessHook(), + DefaultPreCreateHook(p, dockerInput, hostConfig, networkingConfig), + DefaultCopyFileToContainerHook(req.Files), + DefaultLogConsumersHook(req.LogConsumerCfg), + DefaultReadinessHook(), } - req.LifecycleHooks = []ContainerLifecycleHooks{combineContainerHooks(defaultHooks, req.LifecycleHooks)} + req.LifecycleHooks = []ContainerLifecycleHooks{CombineContainerHooks(defaultHooks, req.LifecycleHooks)} err = req.creatingHook(ctx) if err != nil { @@ -1479,8 +1479,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_files_test.go b/docker_files_test.go index d6eb15b804..afdb44822d 100644 --- a/docker_files_test.go +++ b/docker_files_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" ) @@ -21,10 +22,10 @@ func TestCopyFileToContainer(t *testing.T) { t.Fatal(err) } - container, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/bash", - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: absPath, ContainerFilePath: "/hello.sh", @@ -57,10 +58,10 @@ func TestCopyFileToRunningContainer(t *testing.T) { t.Fatal(err) } - container, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/bash:5.2.26", - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: waitForPath, ContainerFilePath: "/waitForHello.sh", @@ -98,10 +99,10 @@ func TestCopyDirectoryToContainer(t *testing.T) { t.Fatal(err) } - container, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/bash", - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: dataDirectory, // ContainerFile cannot create the parent directory, so we copy the scripts @@ -136,10 +137,10 @@ func TestCopyDirectoryToRunningContainerAsFile(t *testing.T) { t.Fatal(err) } - container, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/bash", - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: waitForPath, ContainerFilePath: "/waitForHello.sh", @@ -150,11 +151,15 @@ func TestCopyDirectoryToRunningContainerAsFile(t *testing.T) { }, Started: true, }) - require.NoError(t, err) + if err != nil { + t.Fatal(err) + } // as the container is started, we can create the directory first _, _, err = container.Exec(ctx, []string{"mkdir", "-p", "/scripts"}) - require.NoError(t, err) + if err != nil { + t.Fatal(err) + } // because the container path is a directory, it will use the copy dir method as fallback err = container.CopyFileToContainer(ctx, dataDirectory, "/scripts", 0o700) @@ -182,10 +187,10 @@ func TestCopyDirectoryToRunningContainerAsDir(t *testing.T) { t.Fatal(err) } - container, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ Image: "docker.io/bash", - Files: []ContainerFile{ + Files: []testcontainers.ContainerFile{ { HostFilePath: waitForPath, ContainerFilePath: "/waitForHello.sh", @@ -196,11 +201,15 @@ func TestCopyDirectoryToRunningContainerAsDir(t *testing.T) { }, Started: true, }) - require.NoError(t, err) + if err != nil { + t.Fatal(err) + } // as the container is started, we can create the directory first _, _, err = container.Exec(ctx, []string{"mkdir", "-p", "/scripts"}) - require.NoError(t, err) + if err != nil { + t.Fatal(err) + } err = container.CopyDirToContainer(ctx, dataDirectory, "/scripts", 0o700) if err != nil { diff --git a/docker_mounts.go b/docker_mounts.go index 4906a90692..aed3010361 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..1641c320cb 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,15 @@ 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", + }) + require.NoError(t, err) + + 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 +2022,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 72688876ec..3621115e3a 100644 --- a/generic_test.go +++ b/generic_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) @@ -23,9 +24,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), @@ -50,7 +51,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 @@ -80,9 +81,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), @@ -110,9 +111,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.go b/lifecycle.go index fc1b28e17e..25bfeda087 100644 --- a/lifecycle.go +++ b/lifecycle.go @@ -112,8 +112,8 @@ var DefaultLoggingHook = func(logger Logging) ContainerLifecycleHooks { } } -// defaultPreCreateHook is a hook that will apply the default configuration to the container -var defaultPreCreateHook = func(ctx context.Context, p *DockerProvider, req ContainerRequest, dockerInput *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig) ContainerLifecycleHooks { +// DefaultPreCreateHook is a hook that will apply the default configuration to the container +func DefaultPreCreateHook(p *DockerProvider, dockerInput *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig) ContainerLifecycleHooks { return ContainerLifecycleHooks{ PreCreates: []ContainerRequestHook{ func(ctx context.Context, req ContainerRequest) error { @@ -123,9 +123,9 @@ var defaultPreCreateHook = func(ctx context.Context, p *DockerProvider, req Cont } } -// defaultCopyFileToContainerHook is a hook that will copy files to the container after it's created +// DefaultCopyFileToContainerHook is a hook that will copy files to the container after it's created // but before it's started -var defaultCopyFileToContainerHook = func(files []ContainerFile) ContainerLifecycleHooks { +func DefaultCopyFileToContainerHook(files []ContainerFile) ContainerLifecycleHooks { return ContainerLifecycleHooks{ PostCreates: []ContainerHook{ // copy files to container after it's created @@ -143,8 +143,8 @@ var defaultCopyFileToContainerHook = func(files []ContainerFile) ContainerLifecy } } -// defaultLogConsumersHook is a hook that will start log consumers after the container is started -var defaultLogConsumersHook = func(cfg *LogConsumerConfig) ContainerLifecycleHooks { +// DefaultLogConsumersHook is a hook that will start log consumers after the container is started +func DefaultLogConsumersHook(cfg *LogConsumerConfig) ContainerLifecycleHooks { return ContainerLifecycleHooks{ PostStarts: []ContainerHook{ // first post-start hook is to produce logs and start log consumers @@ -180,8 +180,8 @@ var defaultLogConsumersHook = func(cfg *LogConsumerConfig) ContainerLifecycleHoo } } -// defaultReadinessHook is a hook that will wait for the container to be ready -var defaultReadinessHook = func() ContainerLifecycleHooks { +// DefaultReadinessHook is a hook that will wait for the container to be ready +func DefaultReadinessHook() ContainerLifecycleHooks { return ContainerLifecycleHooks{ PostStarts: []ContainerHook{ // wait for the container to be ready @@ -475,13 +475,13 @@ func (p *DockerProvider) preCreateContainerHook(ctx context.Context, req Contain return nil } -// combineContainerHooks it returns just one ContainerLifecycle hook, as the result of combining +// CombineContainerHooks it returns just one ContainerLifecycle hook, as the result of combining // the default hooks with the user-defined hooks. The function will loop over all the default hooks, // storing each of the hooks in a slice, and then it will loop over all the user-defined hooks, // appending or prepending them to the slice of hooks. The order of hooks is the following: // - for Pre-hooks, always run the default hooks first, then append the user-defined hooks // - for Post-hooks, always run the user-defined hooks first, then the default hooks -func combineContainerHooks(defaultHooks, userDefinedHooks []ContainerLifecycleHooks) ContainerLifecycleHooks { +func CombineContainerHooks(defaultHooks, userDefinedHooks []ContainerLifecycleHooks) ContainerLifecycleHooks { preCreates := []ContainerRequestHook{} postCreates := []ContainerHook{} preStarts := []ContainerHook{} 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 6316df739e..e5f6ee2d7b 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 := testcontainers.DefaultPreCreateHook(provider, 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 := testcontainers.DefaultPreCreateHook(provider, 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 := testcontainers.DefaultPreCreateHook(provider, 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 := testcontainers.DefaultPreCreateHook(provider, 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 := testcontainers.DefaultPreCreateHook(provider, 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 := testcontainers.DefaultPreCreateHook(provider, 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,96 +363,96 @@ 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 }, }, - PostReadies: []ContainerHook{ - func(ctx context.Context, c Container) error { + PostReadies: []testcontainers.ContainerHook{ + func(ctx context.Context, c testcontainers.Container) error { prints = append(prints, fmt.Sprintf("post-ready 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-ready 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 }, @@ -548,7 +466,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, @@ -588,15 +506,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, }) @@ -619,51 +537,51 @@ func TestLifecycleHooks_WithDefaultLogger(t *testing.T) { func TestCombineLifecycleHooks(t *testing.T) { prints := []string{} - preCreateFunc := func(prefix string, hook string, lifecycleID int, hookID int) func(ctx context.Context, req ContainerRequest) error { - return func(ctx context.Context, _ ContainerRequest) error { + preCreateFunc := func(prefix string, hook string, lifecycleID int, hookID int) func(ctx context.Context, req testcontainers.ContainerRequest) error { + return func(ctx context.Context, _ testcontainers.ContainerRequest) error { prints = append(prints, fmt.Sprintf("[%s] pre-%s hook %d.%d", prefix, hook, lifecycleID, hookID)) return nil } } - hookFunc := func(prefix string, hookType string, hook string, lifecycleID int, hookID int) func(ctx context.Context, c Container) error { - return func(ctx context.Context, _ Container) error { + hookFunc := func(prefix string, hookType string, hook string, lifecycleID int, hookID int) func(ctx context.Context, c testcontainers.Container) error { + return func(ctx context.Context, _ testcontainers.Container) error { prints = append(prints, fmt.Sprintf("[%s] %s-%s hook %d.%d", prefix, hookType, hook, lifecycleID, hookID)) return nil } } - preFunc := func(prefix string, hook string, lifecycleID int, hookID int) func(ctx context.Context, c Container) error { + preFunc := func(prefix string, hook string, lifecycleID int, hookID int) func(ctx context.Context, c testcontainers.Container) error { return hookFunc(prefix, "pre", hook, lifecycleID, hookID) } - postFunc := func(prefix string, hook string, lifecycleID int, hookID int) func(ctx context.Context, c Container) error { + postFunc := func(prefix string, hook string, lifecycleID int, hookID int) func(ctx context.Context, c testcontainers.Container) error { return hookFunc(prefix, "post", hook, lifecycleID, hookID) } - lifecycleHookFunc := func(prefix string, lifecycleID int) ContainerLifecycleHooks { - return ContainerLifecycleHooks{ - PreCreates: []ContainerRequestHook{preCreateFunc(prefix, "create", lifecycleID, 1), preCreateFunc(prefix, "create", lifecycleID, 2)}, - PostCreates: []ContainerHook{postFunc(prefix, "create", lifecycleID, 1), postFunc(prefix, "create", lifecycleID, 2)}, - PreStarts: []ContainerHook{preFunc(prefix, "start", lifecycleID, 1), preFunc(prefix, "start", lifecycleID, 2)}, - PostStarts: []ContainerHook{postFunc(prefix, "start", lifecycleID, 1), postFunc(prefix, "start", lifecycleID, 2)}, - PostReadies: []ContainerHook{postFunc(prefix, "ready", lifecycleID, 1), postFunc(prefix, "ready", lifecycleID, 2)}, - PreStops: []ContainerHook{preFunc(prefix, "stop", lifecycleID, 1), preFunc(prefix, "stop", lifecycleID, 2)}, - PostStops: []ContainerHook{postFunc(prefix, "stop", lifecycleID, 1), postFunc(prefix, "stop", lifecycleID, 2)}, - PreTerminates: []ContainerHook{preFunc(prefix, "terminate", lifecycleID, 1), preFunc(prefix, "terminate", lifecycleID, 2)}, - PostTerminates: []ContainerHook{postFunc(prefix, "terminate", lifecycleID, 1), postFunc(prefix, "terminate", lifecycleID, 2)}, + lifecycleHookFunc := func(prefix string, lifecycleID int) testcontainers.ContainerLifecycleHooks { + return testcontainers.ContainerLifecycleHooks{ + PreCreates: []testcontainers.ContainerRequestHook{preCreateFunc(prefix, "create", lifecycleID, 1), preCreateFunc(prefix, "create", lifecycleID, 2)}, + PostCreates: []testcontainers.ContainerHook{postFunc(prefix, "create", lifecycleID, 1), postFunc(prefix, "create", lifecycleID, 2)}, + PreStarts: []testcontainers.ContainerHook{preFunc(prefix, "start", lifecycleID, 1), preFunc(prefix, "start", lifecycleID, 2)}, + PostStarts: []testcontainers.ContainerHook{postFunc(prefix, "start", lifecycleID, 1), postFunc(prefix, "start", lifecycleID, 2)}, + PostReadies: []testcontainers.ContainerHook{postFunc(prefix, "ready", lifecycleID, 1), postFunc(prefix, "ready", lifecycleID, 2)}, + PreStops: []testcontainers.ContainerHook{preFunc(prefix, "stop", lifecycleID, 1), preFunc(prefix, "stop", lifecycleID, 2)}, + PostStops: []testcontainers.ContainerHook{postFunc(prefix, "stop", lifecycleID, 1), postFunc(prefix, "stop", lifecycleID, 2)}, + PreTerminates: []testcontainers.ContainerHook{preFunc(prefix, "terminate", lifecycleID, 1), preFunc(prefix, "terminate", lifecycleID, 2)}, + PostTerminates: []testcontainers.ContainerHook{postFunc(prefix, "terminate", lifecycleID, 1), postFunc(prefix, "terminate", lifecycleID, 2)}, } } - defaultHooks := []ContainerLifecycleHooks{lifecycleHookFunc("default", 1), lifecycleHookFunc("default", 2)} - userDefinedHooks := []ContainerLifecycleHooks{lifecycleHookFunc("user-defined", 1), lifecycleHookFunc("user-defined", 2), lifecycleHookFunc("user-defined", 3)} + defaultHooks := []testcontainers.ContainerLifecycleHooks{lifecycleHookFunc("default", 1), lifecycleHookFunc("default", 2)} + userDefinedHooks := []testcontainers.ContainerLifecycleHooks{lifecycleHookFunc("user-defined", 1), lifecycleHookFunc("user-defined", 2), lifecycleHookFunc("user-defined", 3)} - hooks := combineContainerHooks(defaultHooks, userDefinedHooks) + hooks := testcontainers.CombineContainerHooks(defaultHooks, userDefinedHooks) // call all the hooks in the right order, honouring the lifecycle - req := ContainerRequest{} + req := testcontainers.ContainerRequest{} err := hooks.Creating(context.Background())(req) require.NoError(t, err) - c := &DockerContainer{} + c := &testcontainers.DockerContainer{} err = hooks.Created(context.Background())(c) require.NoError(t, err) @@ -754,15 +672,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, }) @@ -793,7 +711,7 @@ func (l *linesTestLogger) Printf(format string, args ...interface{}) { func TestPrintContainerLogsOnError(t *testing.T) { ctx := context.Background() - 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), @@ -803,7 +721,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, 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 6ec994c635..ecf91943dd 100644 --- a/mounts_test.go +++ b/mounts_test.go @@ -1,4 +1,4 @@ -package testcontainers +package testcontainers_test import ( "context" @@ -7,51 +7,53 @@ 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(), } - expectedLabels := GenericLabels() + expectedLabels := testcontainers.GenericLabels() expectedLabels["hello"] = "world" t.Parallel() tests := []struct { name string - mounts ContainerMounts + mounts testcontainers.ContainerMounts want []mount.Mount }{ { @@ -61,7 +63,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, @@ -73,7 +75,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, @@ -86,9 +88,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, @@ -115,7 +117,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, @@ -124,8 +126,8 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { }, }, { - name: "Single tmpfs mount - read-only", - mounts: ContainerMounts{{Source: GenericTmpfsMountSource{}, Target: "/data", ReadOnly: true}}, + name: "Single volume mount - read-only", + mounts: testcontainers.ContainerMounts{{Source: testcontainers.GenericTmpfsMountSource{}, Target: "/data", ReadOnly: true}}, want: []mount.Mount{ { Type: mount.TypeTmpfs, @@ -136,9 +138,9 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { }, { name: "Single tmpfs mount - with options", - mounts: ContainerMounts{ + mounts: testcontainers.ContainerMounts{ { - Source: DockerTmpfsMountSource{ + Source: testcontainers.DockerTmpfsMountSource{ TmpfsOptions: &mount.TmpfsOptions{ SizeBytes: 50 * 1024 * 1024, Mode: 0o644, @@ -163,18 +165,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", @@ -184,7 +186,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, }) @@ -192,7 +194,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() @@ -202,11 +204,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", @@ -215,7 +217,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, }) @@ -223,11 +225,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{}) }