Skip to content

Commit

Permalink
Merge branch 'main' into v1
Browse files Browse the repository at this point in the history
* main:
  chore(deps): bump github/codeql-action from 3.24.9 to 3.25.15 (#2677)
  fix: use of log.Fatal in main (#2739)
  chore: prepare for next minor development cycle (0.34.0)
  chore: use new version (v0.33.0) in modules and examples
  fix: authentication tests on WSL (#2706)
  fix(registry): compatibility with WSL (#2705)
  • Loading branch information
mdelapenya committed Aug 22, 2024
2 parents 6e6c62c + a3b6173 commit c310222
Show file tree
Hide file tree
Showing 63 changed files with 447 additions and 395 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -67,7 +67,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/autobuild@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
Expand All @@ -80,6 +80,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
with:
category: "/language:${{matrix.language}}"
2 changes: 1 addition & 1 deletion .github/workflows/scorecards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ jobs:

# required for Code scanning alerts
- name: "Upload SARIF results to code scanning"
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
with:
sarif_file: results.sarif
83 changes: 42 additions & 41 deletions auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package auth
import (
"context"
_ "embed"
"encoding/base64"
"fmt"
"path/filepath"
"testing"

Expand Down Expand Up @@ -70,12 +72,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()
Expand All @@ -95,36 +92,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 := ForDockerImage(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 := ForDockerImage(context.Background(), imageReg+imagePath)
require.NoError(t, err)
Expand All @@ -137,17 +121,10 @@ 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 := ForDockerImage(context.Background(), imageReg+imagePath)
require.ErrorIs(t, err, dockercfg.ErrCredentialsNotFound)
Expand All @@ -165,16 +142,9 @@ 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 := ForDockerImage(context.Background(), imageReg+imagePath)
require.ErrorIs(t, err, dockercfg.ErrCredentialsNotFound)
Expand Down Expand Up @@ -212,3 +182,34 @@ func TestGetDockerConfigs(t *testing.T) {
require.Equal(t, expected, got)
})
}

// 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
}
96 changes: 66 additions & 30 deletions docker_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package testcontainers

import (
"context"
"encoding/base64"
"fmt"
"net"
"os"
"testing"

Expand Down Expand Up @@ -45,22 +47,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()

Expand All @@ -69,8 +65,10 @@ func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) {
Context: "./testdata",
Dockerfile: "auth.Dockerfile",
BuildArgs: map[string]*string{
"REGISTRY_PORT": &mappedPort,
"REGISTRY_HOST": &registryHost,
},
Repo: "localhost",
PrintBuildLog: true,
},
AlwaysPullImage: true, // make sure the authentication takes place
ExposedPorts: []string{"6379/tcp"},
Expand All @@ -84,16 +82,10 @@ func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) {
}

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()

Expand All @@ -102,7 +94,7 @@ func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *test
Context: "./testdata",
Dockerfile: "auth.Dockerfile",
BuildArgs: map[string]*string{
"REGISTRY_PORT": &mappedPort,
"REGISTRY_HOST": &registryHost,
},
},
AlwaysPullImage: true, // make sure the authentication takes place
Expand All @@ -117,20 +109,14 @@ func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *test
}

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 := Request{
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"),
Expand Down Expand Up @@ -176,10 +162,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()))
Expand All @@ -188,5 +176,53 @@ func prepareLocalRegistryWithAuth(t *testing.T) string {
_, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)

return mp
return addr
}

// 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 localAddr.IP.String()
}
21 changes: 5 additions & 16 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,14 +663,10 @@ func TestContainerCreationWaitsForLog(t *testing.T) {
}

func TestBuildContainerFromDockerfileWithBuildArgs(t *testing.T) {
t.Log("getting ctx")
ctx := context.Background()

t.Log("got ctx, creating container request")

// fromDockerfileWithBuildArgs {
ba := "build args value"

req := Request{
FromDockerfile: FromDockerfile{
Context: filepath.Join(".", "testdata"),
Expand All @@ -691,23 +687,16 @@ func TestBuildContainerFromDockerfileWithBuildArgs(t *testing.T) {
TerminateContainerOnEnd(t, ctx, c)

ep, err := c.Endpoint(ctx, "http")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)

resp, err := http.Get(ep + "/env")
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}

assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, ba, string(body))
require.NoError(t, err)
require.Equal(t, http.StatusAccepted, resp.StatusCode)
require.Equal(t, ba, string(body))
}

func TestBuildContainerFromDockerfileWithBuildLog(t *testing.T) {
Expand Down
Loading

0 comments on commit c310222

Please sign in to comment.