diff --git a/docker_auth_test.go b/docker_auth_test.go index 5b3e1e1067..4e55d2b9bf 100644 --- a/docker_auth_test.go +++ b/docker_auth_test.go @@ -3,7 +3,9 @@ package testcontainers import ( "context" _ "embed" + "encoding/base64" "fmt" + "net" "os" "path/filepath" "testing" @@ -75,12 +77,7 @@ func TestGetDockerConfig(t *testing.T) { }) t.Run("DOCKER_AUTH_CONFIG env var takes precedence", func(t *testing.T) { - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "`+exampleAuth+`": {} - }, - "credsStore": "desktop" - }`) + setAuthConfig(t, exampleAuth, "", "") t.Setenv("DOCKER_CONFIG", testDockerConfigDirPath) cfg, err := getDockerConfig() @@ -100,36 +97,23 @@ func TestGetDockerConfig(t *testing.T) { }) 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" - }`) + username, password := "gopher", "secret" + creds := setAuthConfig(t, exampleAuth, username, password) 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) + assert.Equal(t, username, cfg.Username) + assert.Equal(t, password, cfg.Password) + assert.Equal(t, creds, 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" - }`) + base64 := setAuthConfig(t, exampleAuth, "gopher", "secret") registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath) require.NoError(t, err) @@ -142,17 +126,11 @@ func TestGetDockerConfig(t *testing.T) { }) 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" - }`) + setAuthConfig(t, invalidRegistryURL, "gopher", "secret") registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath) require.ErrorIs(t, err, dockercfg.ErrCredentialsNotFound) @@ -170,16 +148,10 @@ func TestGetDockerConfig(t *testing.T) { return "" } - base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret imageReg := "" imagePath := "image:latest" - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "example-auth.com": { "username": "gopher", "password": "secret", "auth": "`+base64+`" } - }, - "credsStore": "desktop" - }`) + setAuthConfig(t, "example-auth.com", "gopher", "secret") registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath) require.ErrorIs(t, err, dockercfg.ErrCredentialsNotFound) @@ -219,22 +191,16 @@ func removeImageFromLocalCache(t *testing.T, img string) { Force: true, PruneChildren: true, }) - if err != nil { + if err != nil && !client.IsErrNotFound(err) { t.Logf("could not remove image %s: %v\n", img, err) } } func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) { - mappedPort := prepareLocalRegistryWithAuth(t) + registryHost := prepareLocalRegistryWithAuth(t) // using the same credentials as in the Docker Registry - base64 := "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" // testuser:testpassword - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "localhost:`+mappedPort+`": { "username": "testuser", "password": "testpassword", "auth": "`+base64+`" } - }, - "credsStore": "desktop" - }`) + setAuthConfig(t, registryHost, "testuser", "testpassword") ctx := context.Background() @@ -243,8 +209,10 @@ func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) { Context: "./testdata", Dockerfile: "auth.Dockerfile", BuildArgs: map[string]*string{ - "REGISTRY_PORT": &mappedPort, + "REGISTRY_HOST": ®istryHost, }, + Repo: "localhost", + PrintBuildLog: true, }, AlwaysPullImage: true, // make sure the authentication takes place ExposedPorts: []string{"6379/tcp"}, @@ -252,21 +220,15 @@ func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) { } redisC, err := prepareRedisImage(ctx, req) - require.NoError(t, err) terminateContainerOnEnd(t, ctx, redisC) + require.NoError(t, err) } func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *testing.T) { - mappedPort := prepareLocalRegistryWithAuth(t) + registryHost := prepareLocalRegistryWithAuth(t) // using different credentials than in the Docker Registry - base64 := "Zm9vOmJhcg==" // foo:bar - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "localhost:`+mappedPort+`": { "username": "foo", "password": "bar", "auth": "`+base64+`" } - }, - "credsStore": "desktop" - }`) + setAuthConfig(t, registryHost, "foo", "bar") ctx := context.Background() @@ -275,7 +237,7 @@ func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *test Context: "./testdata", Dockerfile: "auth.Dockerfile", BuildArgs: map[string]*string{ - "REGISTRY_PORT": &mappedPort, + "REGISTRY_HOST": ®istryHost, }, }, AlwaysPullImage: true, // make sure the authentication takes place @@ -284,25 +246,19 @@ func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *test } redisC, err := prepareRedisImage(ctx, req) - require.Error(t, err) terminateContainerOnEnd(t, ctx, redisC) + require.Error(t, err) } func TestCreateContainerFromPrivateRegistry(t *testing.T) { - mappedPort := prepareLocalRegistryWithAuth(t) + registryHost := prepareLocalRegistryWithAuth(t) // using the same credentials as in the Docker Registry - base64 := "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" // testuser:testpassword - t.Setenv("DOCKER_AUTH_CONFIG", `{ - "auths": { - "localhost:`+mappedPort+`": { "username": "testuser", "password": "testpassword", "auth": "`+base64+`" } - }, - "credsStore": "desktop" - }`) + setAuthConfig(t, registryHost, "testuser", "testpassword") ctx := context.Background() req := ContainerRequest{ - Image: "localhost:" + mappedPort + "/redis:5.0-alpine", + Image: registryHost + "/redis:5.0-alpine", AlwaysPullImage: true, // make sure the authentication takes place ExposedPorts: []string{"6379/tcp"}, WaitingFor: wait.ForLog("Ready to accept connections"), @@ -312,8 +268,8 @@ func TestCreateContainerFromPrivateRegistry(t *testing.T) { ContainerRequest: req, Started: true, }) - require.NoError(t, err) terminateContainerOnEnd(t, ctx, redisContainer) + require.NoError(t, err) } func prepareLocalRegistryWithAuth(t *testing.T) string { @@ -356,10 +312,12 @@ func prepareLocalRegistryWithAuth(t *testing.T) string { mappedPort, err := registryC.MappedPort(ctx, "5000/tcp") require.NoError(t, err) + ip := localAddress(t) mp := mappedPort.Port() + addr := ip + ":" + mp t.Cleanup(func() { - removeImageFromLocalCache(t, "localhost:"+mp+"/redis:5.0-alpine") + removeImageFromLocalCache(t, addr+"/redis:5.0-alpine") }) t.Cleanup(func() { require.NoError(t, registryC.Terminate(context.Background())) @@ -368,7 +326,7 @@ func prepareLocalRegistryWithAuth(t *testing.T) string { _, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - return mp + return addr } func prepareRedisImage(ctx context.Context, req ContainerRequest) (Container, error) { @@ -378,9 +336,55 @@ func prepareRedisImage(ctx context.Context, req ContainerRequest) (Container, er Started: true, } - redisC, err := GenericContainer(ctx, genContainerReq) + return GenericContainer(ctx, genContainerReq) +} + +// setAuthConfig sets the DOCKER_AUTH_CONFIG environment variable with +// authentication for with the given host, username and password. +// It returns the base64 encoded credentials. +func setAuthConfig(t *testing.T, host, username, password string) string { + t.Helper() + + var creds string + if username != "" || password != "" { + creds = base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + } + + auth := fmt.Sprintf(`{ + "auths": { + %q: { + "username": %q, + "password": %q, + "auth": %q + } + }, + "credsStore": "desktop" +}`, + host, + username, + password, + creds, + ) + t.Setenv("DOCKER_AUTH_CONFIG", auth) + + return creds +} + +// localAddress returns the local address of the machine +// which can be used to connect to the local registry. +// This avoids the issues with localhost on WSL. +func localAddress(t *testing.T) string { + if os.Getenv("WSL_DISTRO_NAME") == "" { + return "localhost" + } + + conn, err := net.Dial("udp", "golang.org:80") + require.NoError(t, err) + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.UDPAddr) - return redisC, err + return localAddr.IP.String() } //go:embed testdata/.docker/config.json diff --git a/testdata/auth.Dockerfile b/testdata/auth.Dockerfile index 367e228a67..5fcebe0baf 100644 --- a/testdata/auth.Dockerfile +++ b/testdata/auth.Dockerfile @@ -1,3 +1,3 @@ -ARG REGISTRY_PORT=5001 +ARG REGISTRY_HOST=localhost:5001 -FROM localhost:${REGISTRY_PORT}/redis:5.0-alpine +FROM ${REGISTRY_HOST}/redis:5.0-alpine diff --git a/testhelpers_test.go b/testhelpers_test.go index 47bbcb54c3..3be3b7c50d 100644 --- a/testhelpers_test.go +++ b/testhelpers_test.go @@ -20,7 +20,6 @@ func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr testcontain return } tb.Cleanup(func() { - tb.Log("terminating container") require.NoError(tb, ctr.Terminate(ctx)) }) }