From 67613e20ef287bbd958bb79256f7c31653c431bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 13:11:29 +0100 Subject: [PATCH 01/37] chore: move TestContainers config to a separate file --- config.go | 50 +++++++++ config_test.go | 267 +++++++++++++++++++++++++++++++++++++++++++++++++ docker.go | 43 -------- docker_test.go | 257 ----------------------------------------------- 4 files changed, 317 insertions(+), 300 deletions(-) create mode 100644 config.go create mode 100644 config_test.go diff --git a/config.go b/config.go new file mode 100644 index 0000000000..3ce9e24f0f --- /dev/null +++ b/config.go @@ -0,0 +1,50 @@ +package testcontainers + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/magiconair/properties" +) + +type TestContainersConfig struct { + Host string `properties:"docker.host,default="` + TLSVerify int `properties:"docker.tls.verify,default=0"` + CertPath string `properties:"docker.cert.path,default="` + RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` +} + +// configureTC reads from testcontainers properties file, if it exists +// it is possible that certain values get overridden when set as environment variables +func configureTC() TestContainersConfig { + config := TestContainersConfig{} + + applyEnvironmentConfiguration := func(config TestContainersConfig) TestContainersConfig { + ryukPrivilegedEnv := os.Getenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED") + if ryukPrivilegedEnv != "" { + config.RyukPrivileged = ryukPrivilegedEnv == "true" + } + + return config + } + + home, err := os.UserHomeDir() + if err != nil { + return applyEnvironmentConfiguration(config) + } + + tcProp := filepath.Join(home, ".testcontainers.properties") + // init from a file + properties, err := properties.LoadFile(tcProp, properties.UTF8) + if err != nil { + return applyEnvironmentConfiguration(config) + } + + if err := properties.Decode(&config); err != nil { + fmt.Printf("invalid testcontainers properties file, returning an empty Testcontainers configuration: %v\n", err) + return applyEnvironmentConfiguration(config) + } + + return applyEnvironmentConfiguration(config) +} diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000000..eb8d2244a6 --- /dev/null +++ b/config_test.go @@ -0,0 +1,267 @@ +package testcontainers + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadTCPropsFile(t *testing.T) { + t.Run("HOME is not set", func(t *testing.T) { + t.Setenv("HOME", "") + + config := configureTC() + + assert.Empty(t, config, "TC props file should not exist") + }) + + t.Run("HOME is not set - TESTCONTAINERS_ env is set", func(t *testing.T) { + t.Setenv("HOME", "") + t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") + + config := configureTC() + + expected := TestContainersConfig{} + expected.RyukPrivileged = true + + assert.Equal(t, expected, config) + }) + + t.Run("HOME does not contain TC props file", func(t *testing.T) { + tmpDir := t.TempDir() + t.Setenv("HOME", tmpDir) + + config := configureTC() + + assert.Empty(t, config, "TC props file should not exist") + }) + + t.Run("HOME does not contain TC props file - TESTCONTAINERS_ env is set", func(t *testing.T) { + tmpDir := t.TempDir() + t.Setenv("HOME", tmpDir) + t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") + + config := configureTC() + expected := TestContainersConfig{} + expected.RyukPrivileged = true + + assert.Equal(t, expected, config) + }) + + t.Run("HOME contains TC properties file", func(t *testing.T) { + tests := []struct { + content string + env map[string]string + expected TestContainersConfig + }{ + { + "docker.host = tcp://127.0.0.1:33293", + map[string]string{}, + TestContainersConfig{ + Host: "tcp://127.0.0.1:33293", + TLSVerify: 0, + CertPath: "", + }, + }, + { + "docker.host = tcp://127.0.0.1:33293", + map[string]string{}, + TestContainersConfig{ + Host: "tcp://127.0.0.1:33293", + TLSVerify: 0, + CertPath: "", + }, + }, + { + `docker.host = tcp://127.0.0.1:33293 + docker.host = tcp://127.0.0.1:4711 + `, + map[string]string{}, + TestContainersConfig{ + Host: "tcp://127.0.0.1:4711", + TLSVerify: 0, + CertPath: "", + }, + }, + { + `docker.host = tcp://127.0.0.1:33293 + docker.host = tcp://127.0.0.1:4711 + docker.host = tcp://127.0.0.1:1234 + docker.tls.verify = 1 + `, + map[string]string{}, + TestContainersConfig{ + Host: "tcp://127.0.0.1:1234", + TLSVerify: 1, + CertPath: "", + }, + }, + { + "", + map[string]string{}, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + }, + }, + { + `foo = bar + docker.host = tcp://127.0.0.1:1234 + `, + map[string]string{}, + TestContainersConfig{ + Host: "tcp://127.0.0.1:1234", + TLSVerify: 0, + CertPath: "", + }, + }, + { + "docker.host=tcp://127.0.0.1:33293", + map[string]string{}, + TestContainersConfig{ + Host: "tcp://127.0.0.1:33293", + TLSVerify: 0, + CertPath: "", + }, + }, + { + `#docker.host=tcp://127.0.0.1:33293`, + map[string]string{}, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + }, + }, + { + `#docker.host = tcp://127.0.0.1:33293 + docker.host = tcp://127.0.0.1:4711 + docker.host = tcp://127.0.0.1:1234 + docker.cert.path=/tmp/certs`, + map[string]string{}, + TestContainersConfig{ + Host: "tcp://127.0.0.1:1234", + TLSVerify: 0, + CertPath: "/tmp/certs", + }, + }, + { + `ryuk.container.privileged=true`, + map[string]string{}, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: true, + }, + }, + { + ``, + map[string]string{ + "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", + }, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: true, + }, + }, + { + `ryuk.container.privileged=true`, + map[string]string{ + "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", + }, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: true, + }, + }, + { + `ryuk.container.privileged=false`, + map[string]string{ + "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", + }, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: true, + }, + }, + { + `ryuk.container.privileged=true`, + map[string]string{ + "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", + }, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: false, + }, + }, + { + `ryuk.container.privileged=false`, + map[string]string{ + "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", + }, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: false, + }, + }, + { + `ryuk.container.privileged=false + docker.tls.verify = ERROR`, + map[string]string{ + "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", + }, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: true, + }, + }, + { + `ryuk.container.privileged=false`, + map[string]string{ + "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "foo", + }, + TestContainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukPrivileged: false, + }, + }, + } + for i, tt := range tests { + t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + tmpDir := t.TempDir() + t.Setenv("HOME", tmpDir) + for k, v := range tt.env { + t.Setenv(k, v) + } + if err := os.WriteFile(filepath.Join(tmpDir, ".testcontainers.properties"), []byte(tt.content), 0o600); err != nil { + t.Errorf("Failed to create the file: %v", err) + return + } + + config := configureTC() + + assert.Equal(t, tt.expected, config, "Configuration doesn't not match") + + }) + } + }) +} diff --git a/docker.go b/docker.go index 73ebf83d5f..9335d43119 100644 --- a/docker.go +++ b/docker.go @@ -30,7 +30,6 @@ import ( "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/go-connections/nat" "github.com/google/uuid" - "github.com/magiconair/properties" "github.com/moby/term" specs "github.com/opencontainers/image-spec/specs-go/v1" @@ -746,14 +745,6 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) -// or through Decode -type TestContainersConfig struct { - Host string `properties:"docker.host,default="` - TLSVerify int `properties:"docker.tls.verify,default=0"` - CertPath string `properties:"docker.cert.path,default="` - RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` -} - func NewDockerClient() (cli *client.Client, host string, tcConfig TestContainersConfig, err error) { tcConfig = configureTC() @@ -801,40 +792,6 @@ func NewDockerClient() (cli *client.Client, host string, tcConfig TestContainers return cli, host, tcConfig, nil } -// configureTC reads from testcontainers properties file, if it exists -// it is possible that certain values get overridden when set as environment variables -func configureTC() TestContainersConfig { - config := TestContainersConfig{} - - applyEnvironmentConfiguration := func(config TestContainersConfig) TestContainersConfig { - ryukPrivilegedEnv := os.Getenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED") - if ryukPrivilegedEnv != "" { - config.RyukPrivileged = ryukPrivilegedEnv == "true" - } - - return config - } - - home, err := os.UserHomeDir() - if err != nil { - return applyEnvironmentConfiguration(config) - } - - tcProp := filepath.Join(home, ".testcontainers.properties") - // init from a file - properties, err := properties.LoadFile(tcProp, properties.UTF8) - if err != nil { - return applyEnvironmentConfiguration(config) - } - - if err := properties.Decode(&config); err != nil { - fmt.Printf("invalid testcontainers properties file, returning an empty Testcontainers configuration: %v\n", err) - return applyEnvironmentConfiguration(config) - } - - return applyEnvironmentConfiguration(config) -} - // BuildImage will build and image from context and Dockerfile, then return the tag func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (string, error) { repo := uuid.New() diff --git a/docker_test.go b/docker_test.go index 3e23441e35..612d3267aa 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1280,263 +1280,6 @@ func TestEntrypoint(t *testing.T) { terminateContainerOnEnd(t, ctx, c) } -func TestReadTCPropsFile(t *testing.T) { - t.Run("HOME is not set", func(t *testing.T) { - t.Setenv("HOME", "") - - config := configureTC() - - assert.Empty(t, config, "TC props file should not exist") - }) - - t.Run("HOME is not set - TESTCONTAINERS_ env is set", func(t *testing.T) { - t.Setenv("HOME", "") - t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - - config := configureTC() - - expected := TestContainersConfig{} - expected.RyukPrivileged = true - - assert.Equal(t, expected, config) - }) - - t.Run("HOME does not contain TC props file", func(t *testing.T) { - tmpDir := t.TempDir() - t.Setenv("HOME", tmpDir) - - config := configureTC() - - assert.Empty(t, config, "TC props file should not exist") - }) - - t.Run("HOME does not contain TC props file - TESTCONTAINERS_ env is set", func(t *testing.T) { - tmpDir := t.TempDir() - t.Setenv("HOME", tmpDir) - t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - - config := configureTC() - expected := TestContainersConfig{} - expected.RyukPrivileged = true - - assert.Equal(t, expected, config) - }) - - t.Run("HOME contains TC properties file", func(t *testing.T) { - tests := []struct { - content string - env map[string]string - expected TestContainersConfig - }{ - { - "docker.host = tcp://127.0.0.1:33293", - map[string]string{}, - TestContainersConfig{ - Host: "tcp://127.0.0.1:33293", - TLSVerify: 0, - CertPath: "", - }, - }, - { - "docker.host = tcp://127.0.0.1:33293", - map[string]string{}, - TestContainersConfig{ - Host: "tcp://127.0.0.1:33293", - TLSVerify: 0, - CertPath: "", - }, - }, - { - `docker.host = tcp://127.0.0.1:33293 - docker.host = tcp://127.0.0.1:4711 - `, - map[string]string{}, - TestContainersConfig{ - Host: "tcp://127.0.0.1:4711", - TLSVerify: 0, - CertPath: "", - }, - }, - { - `docker.host = tcp://127.0.0.1:33293 - docker.host = tcp://127.0.0.1:4711 - docker.host = tcp://127.0.0.1:1234 - docker.tls.verify = 1 - `, - map[string]string{}, - TestContainersConfig{ - Host: "tcp://127.0.0.1:1234", - TLSVerify: 1, - CertPath: "", - }, - }, - { - "", - map[string]string{}, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - }, - }, - { - `foo = bar - docker.host = tcp://127.0.0.1:1234 - `, - map[string]string{}, - TestContainersConfig{ - Host: "tcp://127.0.0.1:1234", - TLSVerify: 0, - CertPath: "", - }, - }, - { - "docker.host=tcp://127.0.0.1:33293", - map[string]string{}, - TestContainersConfig{ - Host: "tcp://127.0.0.1:33293", - TLSVerify: 0, - CertPath: "", - }, - }, - { - `#docker.host=tcp://127.0.0.1:33293`, - map[string]string{}, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - }, - }, - { - `#docker.host = tcp://127.0.0.1:33293 - docker.host = tcp://127.0.0.1:4711 - docker.host = tcp://127.0.0.1:1234 - docker.cert.path=/tmp/certs`, - map[string]string{}, - TestContainersConfig{ - Host: "tcp://127.0.0.1:1234", - TLSVerify: 0, - CertPath: "/tmp/certs", - }, - }, - { - `ryuk.container.privileged=true`, - map[string]string{}, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: true, - }, - }, - { - ``, - map[string]string{ - "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", - }, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: true, - }, - }, - { - `ryuk.container.privileged=true`, - map[string]string{ - "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", - }, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: true, - }, - }, - { - `ryuk.container.privileged=false`, - map[string]string{ - "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", - }, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: true, - }, - }, - { - `ryuk.container.privileged=true`, - map[string]string{ - "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", - }, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: false, - }, - }, - { - `ryuk.container.privileged=false`, - map[string]string{ - "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", - }, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: false, - }, - }, - { - `ryuk.container.privileged=false - docker.tls.verify = ERROR`, - map[string]string{ - "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", - }, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: true, - }, - }, - { - `ryuk.container.privileged=false`, - map[string]string{ - "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "foo", - }, - TestContainersConfig{ - Host: "", - TLSVerify: 0, - CertPath: "", - RyukPrivileged: false, - }, - }, - } - for i, tt := range tests { - t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { - tmpDir := t.TempDir() - t.Setenv("HOME", tmpDir) - for k, v := range tt.env { - t.Setenv(k, v) - } - if err := os.WriteFile(filepath.Join(tmpDir, ".testcontainers.properties"), []byte(tt.content), 0o600); err != nil { - t.Errorf("Failed to create the file: %v", err) - return - } - - config := configureTC() - - assert.Equal(t, tt.expected, config, "Configuration doesn't not match") - - }) - } - }) -} - func ExampleDockerProvider_CreateContainer() { ctx := context.Background() req := ContainerRequest{ From 69d5fff0801880b5380a36a38cd00214a3585ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 13:13:13 +0100 Subject: [PATCH 02/37] chore: rename to readConfig --- config.go | 4 ++-- config_test.go | 12 ++++++------ docker.go | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config.go b/config.go index 3ce9e24f0f..9439196c58 100644 --- a/config.go +++ b/config.go @@ -15,9 +15,9 @@ type TestContainersConfig struct { RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` } -// configureTC reads from testcontainers properties file, if it exists +// readConfig reads from testcontainers properties file, if it exists // it is possible that certain values get overridden when set as environment variables -func configureTC() TestContainersConfig { +func readConfig() TestContainersConfig { config := TestContainersConfig{} applyEnvironmentConfiguration := func(config TestContainersConfig) TestContainersConfig { diff --git a/config_test.go b/config_test.go index eb8d2244a6..e30599c72e 100644 --- a/config_test.go +++ b/config_test.go @@ -9,11 +9,11 @@ import ( "github.com/stretchr/testify/assert" ) -func TestReadTCPropsFile(t *testing.T) { +func TestReadTCConfig(t *testing.T) { t.Run("HOME is not set", func(t *testing.T) { t.Setenv("HOME", "") - config := configureTC() + config := readConfig() assert.Empty(t, config, "TC props file should not exist") }) @@ -22,7 +22,7 @@ func TestReadTCPropsFile(t *testing.T) { t.Setenv("HOME", "") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - config := configureTC() + config := readConfig() expected := TestContainersConfig{} expected.RyukPrivileged = true @@ -34,7 +34,7 @@ func TestReadTCPropsFile(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) - config := configureTC() + config := readConfig() assert.Empty(t, config, "TC props file should not exist") }) @@ -44,7 +44,7 @@ func TestReadTCPropsFile(t *testing.T) { t.Setenv("HOME", tmpDir) t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - config := configureTC() + config := readConfig() expected := TestContainersConfig{} expected.RyukPrivileged = true @@ -257,7 +257,7 @@ func TestReadTCPropsFile(t *testing.T) { return } - config := configureTC() + config := readConfig() assert.Equal(t, tt.expected, config, "Configuration doesn't not match") diff --git a/docker.go b/docker.go index 9335d43119..5feaabcf5c 100644 --- a/docker.go +++ b/docker.go @@ -746,7 +746,7 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) func NewDockerClient() (cli *client.Client, host string, tcConfig TestContainersConfig, err error) { - tcConfig = configureTC() + tcConfig = readConfig() host = tcConfig.Host From 1e0a3fb8b77e26a8da8a44eecbe0f873c41f0c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 16:00:26 +0100 Subject: [PATCH 03/37] break: rename struct to honour project's name --- config.go | 9 +++++---- config_test.go | 40 ++++++++++++++++++++-------------------- docker.go | 12 ++++++------ provider.go | 2 +- reaper.go | 2 +- reaper_test.go | 14 +++++++------- 6 files changed, 40 insertions(+), 39 deletions(-) diff --git a/config.go b/config.go index 9439196c58..dc65dc641e 100644 --- a/config.go +++ b/config.go @@ -8,7 +8,8 @@ import ( "github.com/magiconair/properties" ) -type TestContainersConfig struct { +// TestcontainersConfig represents the configuration for Testcontainers +type TestcontainersConfig struct { Host string `properties:"docker.host,default="` TLSVerify int `properties:"docker.tls.verify,default=0"` CertPath string `properties:"docker.cert.path,default="` @@ -17,10 +18,10 @@ type TestContainersConfig struct { // readConfig reads from testcontainers properties file, if it exists // it is possible that certain values get overridden when set as environment variables -func readConfig() TestContainersConfig { - config := TestContainersConfig{} +func readConfig() TestcontainersConfig { + config := TestcontainersConfig{} - applyEnvironmentConfiguration := func(config TestContainersConfig) TestContainersConfig { + applyEnvironmentConfiguration := func(config TestcontainersConfig) TestcontainersConfig { ryukPrivilegedEnv := os.Getenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED") if ryukPrivilegedEnv != "" { config.RyukPrivileged = ryukPrivilegedEnv == "true" diff --git a/config_test.go b/config_test.go index e30599c72e..091dbde909 100644 --- a/config_test.go +++ b/config_test.go @@ -24,7 +24,7 @@ func TestReadTCConfig(t *testing.T) { config := readConfig() - expected := TestContainersConfig{} + expected := TestcontainersConfig{} expected.RyukPrivileged = true assert.Equal(t, expected, config) @@ -45,7 +45,7 @@ func TestReadTCConfig(t *testing.T) { t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") config := readConfig() - expected := TestContainersConfig{} + expected := TestcontainersConfig{} expected.RyukPrivileged = true assert.Equal(t, expected, config) @@ -55,12 +55,12 @@ func TestReadTCConfig(t *testing.T) { tests := []struct { content string env map[string]string - expected TestContainersConfig + expected TestcontainersConfig }{ { "docker.host = tcp://127.0.0.1:33293", map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "tcp://127.0.0.1:33293", TLSVerify: 0, CertPath: "", @@ -69,7 +69,7 @@ func TestReadTCConfig(t *testing.T) { { "docker.host = tcp://127.0.0.1:33293", map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "tcp://127.0.0.1:33293", TLSVerify: 0, CertPath: "", @@ -80,7 +80,7 @@ func TestReadTCConfig(t *testing.T) { docker.host = tcp://127.0.0.1:4711 `, map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "tcp://127.0.0.1:4711", TLSVerify: 0, CertPath: "", @@ -93,7 +93,7 @@ func TestReadTCConfig(t *testing.T) { docker.tls.verify = 1 `, map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "tcp://127.0.0.1:1234", TLSVerify: 1, CertPath: "", @@ -102,7 +102,7 @@ func TestReadTCConfig(t *testing.T) { { "", map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -113,7 +113,7 @@ func TestReadTCConfig(t *testing.T) { docker.host = tcp://127.0.0.1:1234 `, map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "tcp://127.0.0.1:1234", TLSVerify: 0, CertPath: "", @@ -122,7 +122,7 @@ func TestReadTCConfig(t *testing.T) { { "docker.host=tcp://127.0.0.1:33293", map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "tcp://127.0.0.1:33293", TLSVerify: 0, CertPath: "", @@ -131,7 +131,7 @@ func TestReadTCConfig(t *testing.T) { { `#docker.host=tcp://127.0.0.1:33293`, map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -143,7 +143,7 @@ func TestReadTCConfig(t *testing.T) { docker.host = tcp://127.0.0.1:1234 docker.cert.path=/tmp/certs`, map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "tcp://127.0.0.1:1234", TLSVerify: 0, CertPath: "/tmp/certs", @@ -152,7 +152,7 @@ func TestReadTCConfig(t *testing.T) { { `ryuk.container.privileged=true`, map[string]string{}, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -164,7 +164,7 @@ func TestReadTCConfig(t *testing.T) { map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -176,7 +176,7 @@ func TestReadTCConfig(t *testing.T) { map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -188,7 +188,7 @@ func TestReadTCConfig(t *testing.T) { map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -200,7 +200,7 @@ func TestReadTCConfig(t *testing.T) { map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", }, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -212,7 +212,7 @@ func TestReadTCConfig(t *testing.T) { map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", }, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -225,7 +225,7 @@ func TestReadTCConfig(t *testing.T) { map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", @@ -237,7 +237,7 @@ func TestReadTCConfig(t *testing.T) { map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "foo", }, - TestContainersConfig{ + TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", diff --git a/docker.go b/docker.go index 5feaabcf5c..9d58779982 100644 --- a/docker.go +++ b/docker.go @@ -721,7 +721,7 @@ type DockerProvider struct { client client.APIClient host string hostCache string - config TestContainersConfig + config TestcontainersConfig } // Client gets the docker client used by the provider @@ -745,7 +745,7 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) -func NewDockerClient() (cli *client.Client, host string, tcConfig TestContainersConfig, err error) { +func NewDockerClient() (cli *client.Client, host string, tcConfig TestcontainersConfig, err error) { tcConfig = readConfig() host = tcConfig.Host @@ -776,7 +776,7 @@ func NewDockerClient() (cli *client.Client, host string, tcConfig TestContainers cli, err = client.NewClientWithOpts(opts...) if err != nil { - return nil, "", TestContainersConfig{}, err + return nil, "", TestcontainersConfig{}, err } _, err = cli.Ping(context.TODO()) @@ -784,7 +784,7 @@ func NewDockerClient() (cli *client.Client, host string, tcConfig TestContainers // fallback to environment cli, err = testcontainersdocker.NewClient(context.Background()) if err != nil { - return nil, "", TestContainersConfig{}, err + return nil, "", TestcontainersConfig{}, err } } defer cli.Close() @@ -1167,9 +1167,9 @@ func (p *DockerProvider) RunContainer(ctx context.Context, req ContainerRequest) return c, nil } -// Config provides the TestContainersConfig read from $HOME/.testcontainers.properties or +// Config provides the TestcontainersConfig read from $HOME/.testcontainers.properties or // the environment variables -func (p *DockerProvider) Config() TestContainersConfig { +func (p *DockerProvider) Config() TestcontainersConfig { return p.config } diff --git a/provider.go b/provider.go index 479a287117..2f3231556a 100644 --- a/provider.go +++ b/provider.go @@ -84,7 +84,7 @@ type ContainerProvider interface { ReuseOrCreateContainer(context.Context, ContainerRequest) (Container, error) // reuses a container if it exists or creates a container without starting RunContainer(context.Context, ContainerRequest) (Container, error) // create a container and start it Health(context.Context) error - Config() TestContainersConfig + Config() TestcontainersConfig } // GetProvider provides the provider implementation for a certain type diff --git a/reaper.go b/reaper.go index e36a46680d..9ee6f293d6 100644 --- a/reaper.go +++ b/reaper.go @@ -37,7 +37,7 @@ var ( // The ContainerProvider interface should usually satisfy this as well, so it is pluggable type ReaperProvider interface { RunContainer(ctx context.Context, req ContainerRequest) (Container, error) - Config() TestContainersConfig + Config() TestcontainersConfig } // NewReaper creates a Reaper with a sessionID to identify containers and a provider to use diff --git a/reaper_test.go b/reaper_test.go index 1032c88ee4..17f7258fc3 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -18,7 +18,7 @@ type mockReaperProvider struct { req ContainerRequest hostConfig *container.HostConfig enpointSettings map[string]*network.EndpointSettings - config TestContainersConfig + config TestcontainersConfig } var errExpected = errors.New("expected") @@ -43,7 +43,7 @@ func (m *mockReaperProvider) RunContainer(ctx context.Context, req ContainerRequ return nil, errExpected } -func (m *mockReaperProvider) Config() TestContainersConfig { +func (m *mockReaperProvider) Config() TestcontainersConfig { return m.config } @@ -78,7 +78,7 @@ func Test_NewReaper(t *testing.T) { type cases struct { name string req ContainerRequest - config TestContainersConfig + config TestcontainersConfig ctx context.Context } @@ -86,7 +86,7 @@ func Test_NewReaper(t *testing.T) { { name: "non-privileged", req: createContainerRequest(nil), - config: TestContainersConfig{}, + config: TestcontainersConfig{}, }, { name: "privileged", @@ -94,7 +94,7 @@ func Test_NewReaper(t *testing.T) { req.Privileged = true return req }), - config: TestContainersConfig{ + config: TestcontainersConfig{ RyukPrivileged: true, }, }, @@ -104,7 +104,7 @@ func Test_NewReaper(t *testing.T) { req.Mounts = Mounts(BindMount("/value/in/context.sock", "/var/run/docker.sock")) return req }), - config: TestContainersConfig{}, + config: TestcontainersConfig{}, ctx: context.WithValue(context.TODO(), testcontainersdocker.DockerHostContextKey, "unix:///value/in/context.sock"), }, } @@ -153,7 +153,7 @@ func Test_ReaperForNetwork(t *testing.T) { } provider := &mockReaperProvider{ - config: TestContainersConfig{}, + config: TestcontainersConfig{}, } _, err := newReaper(ctx, "sessionId", provider, req.ReaperOptions...) From f64f9384cc2a5c8fcf086d584e1c49c9479658bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 17:15:47 +0100 Subject: [PATCH 04/37] chore: add names to test table --- config_test.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/config_test.go b/config_test.go index 091dbde909..ec9382870c 100644 --- a/config_test.go +++ b/config_test.go @@ -53,11 +53,13 @@ func TestReadTCConfig(t *testing.T) { t.Run("HOME contains TC properties file", func(t *testing.T) { tests := []struct { + name string content string env map[string]string expected TestcontainersConfig }{ { + "Single Docker host with spaces", "docker.host = tcp://127.0.0.1:33293", map[string]string{}, TestcontainersConfig{ @@ -67,15 +69,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { - "docker.host = tcp://127.0.0.1:33293", - map[string]string{}, - TestcontainersConfig{ - Host: "tcp://127.0.0.1:33293", - TLSVerify: 0, - CertPath: "", - }, - }, - { + "Multiple docker host entries, last one wins", `docker.host = tcp://127.0.0.1:33293 docker.host = tcp://127.0.0.1:4711 `, @@ -87,6 +81,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "Multiple docker host entries, last one wins, with TLS", `docker.host = tcp://127.0.0.1:33293 docker.host = tcp://127.0.0.1:4711 docker.host = tcp://127.0.0.1:1234 @@ -100,6 +95,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "Empty file", "", map[string]string{}, TestcontainersConfig{ @@ -109,6 +105,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "Non-valid properties are ignored", `foo = bar docker.host = tcp://127.0.0.1:1234 `, @@ -120,6 +117,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "Single Docker host without spaces", "docker.host=tcp://127.0.0.1:33293", map[string]string{}, TestcontainersConfig{ @@ -129,6 +127,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "Comments are ignored", `#docker.host=tcp://127.0.0.1:33293`, map[string]string{}, TestcontainersConfig{ @@ -138,6 +137,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "Multiple docker host entries, last one wins, with TLS and cert path", `#docker.host = tcp://127.0.0.1:33293 docker.host = tcp://127.0.0.1:4711 docker.host = tcp://127.0.0.1:1234 @@ -150,6 +150,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With Ryuk container privileged using properties", `ryuk.container.privileged=true`, map[string]string{}, TestcontainersConfig{ @@ -160,6 +161,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With Ryuk container privileged using an env var", ``, map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", @@ -172,6 +174,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With Ryuk container privileged using an env var and properties. Env var wins (0)", `ryuk.container.privileged=true`, map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", @@ -184,6 +187,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With Ryuk container privileged using an env var and properties. Env var wins (1)", `ryuk.container.privileged=false`, map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", @@ -196,6 +200,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With Ryuk container privileged using an env var and properties. Env var wins (2)", `ryuk.container.privileged=true`, map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", @@ -208,6 +213,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With Ryuk container privileged using an env var and properties. Env var wins (3)", `ryuk.container.privileged=false`, map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", @@ -220,6 +226,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With TLS verify using properties when value is wrong", `ryuk.container.privileged=false docker.tls.verify = ERROR`, map[string]string{ @@ -233,6 +240,7 @@ func TestReadTCConfig(t *testing.T) { }, }, { + "With Ryuk container privileged using an env var and properties. Env var does not win because it's not a boolean value", `ryuk.container.privileged=false`, map[string]string{ "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "foo", @@ -245,8 +253,8 @@ func TestReadTCConfig(t *testing.T) { }, }, } - for i, tt := range tests { - t.Run(fmt.Sprintf("[%d]", i), func(t *testing.T) { + for _, tt := range tests { + t.Run(fmt.Sprintf(tt.name), func(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) for k, v := range tt.env { From 7f45a6198fc9dc9656d5ccfbc6a58aa213821ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 17:21:59 +0100 Subject: [PATCH 05/37] chore: support for disabling Ryuk at the configuration layer --- config.go | 16 ++++++++- config_test.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/config.go b/config.go index dc65dc641e..960fc6ab34 100644 --- a/config.go +++ b/config.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" "github.com/magiconair/properties" ) @@ -13,6 +14,7 @@ type TestcontainersConfig struct { Host string `properties:"docker.host,default="` TLSVerify int `properties:"docker.tls.verify,default=0"` CertPath string `properties:"docker.cert.path,default="` + RyukDisabled bool `properties:"ryuk.disabled,default=false"` RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` } @@ -22,8 +24,13 @@ func readConfig() TestcontainersConfig { config := TestcontainersConfig{} applyEnvironmentConfiguration := func(config TestcontainersConfig) TestcontainersConfig { + ryukDisabledEnv := os.Getenv("TESTCONTAINERS_RYUK_DISABLED") + if parseBool(ryukDisabledEnv) { + config.RyukDisabled = ryukDisabledEnv == "true" + } + ryukPrivilegedEnv := os.Getenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED") - if ryukPrivilegedEnv != "" { + if parseBool(ryukPrivilegedEnv) { config.RyukPrivileged = ryukPrivilegedEnv == "true" } @@ -49,3 +56,10 @@ func readConfig() TestcontainersConfig { return applyEnvironmentConfiguration(config) } + +func parseBool(input string) bool { + if _, err := strconv.ParseBool(input); err == nil { + return true + } + return false +} diff --git a/config_test.go b/config_test.go index ec9382870c..4aaaaf4e0e 100644 --- a/config_test.go +++ b/config_test.go @@ -20,11 +20,13 @@ func TestReadTCConfig(t *testing.T) { t.Run("HOME is not set - TESTCONTAINERS_ env is set", func(t *testing.T) { t.Setenv("HOME", "") + t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") config := readConfig() expected := TestcontainersConfig{} + expected.RyukDisabled = true expected.RyukPrivileged = true assert.Equal(t, expected, config) @@ -42,10 +44,12 @@ func TestReadTCConfig(t *testing.T) { t.Run("HOME does not contain TC props file - TESTCONTAINERS_ env is set", func(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) + t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") config := readConfig() expected := TestcontainersConfig{} + expected.RyukDisabled = true expected.RyukPrivileged = true assert.Equal(t, expected, config) @@ -149,6 +153,17 @@ func TestReadTCConfig(t *testing.T) { CertPath: "/tmp/certs", }, }, + { + "With Ryuk disabled using properties", + `ryuk.disabled=true`, + map[string]string{}, + TestcontainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukDisabled: true, + }, + }, { "With Ryuk container privileged using properties", `ryuk.container.privileged=true`, @@ -160,6 +175,19 @@ func TestReadTCConfig(t *testing.T) { RyukPrivileged: true, }, }, + { + "With Ryuk disabled using an env var", + ``, + map[string]string{ + "TESTCONTAINERS_RYUK_DISABLED": "true", + }, + TestcontainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukDisabled: true, + }, + }, { "With Ryuk container privileged using an env var", ``, @@ -173,6 +201,58 @@ func TestReadTCConfig(t *testing.T) { RyukPrivileged: true, }, }, + { + "With Ryuk disabled using an env var and properties. Env var wins (0)", + `ryuk.disabled=true`, + map[string]string{ + "TESTCONTAINERS_RYUK_DISABLED": "true", + }, + TestcontainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukDisabled: true, + }, + }, + { + "With Ryuk disabled using an env var and properties. Env var wins (1)", + `ryuk.disabled=false`, + map[string]string{ + "TESTCONTAINERS_RYUK_DISABLED": "true", + }, + TestcontainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukDisabled: true, + }, + }, + { + "With Ryuk disabled using an env var and properties. Env var wins (2)", + `ryuk.disabled=true`, + map[string]string{ + "TESTCONTAINERS_RYUK_DISABLED": "false", + }, + TestcontainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukDisabled: false, + }, + }, + { + "With Ryuk disabled using an env var and properties. Env var wins (3)", + `ryuk.disabled=false`, + map[string]string{ + "TESTCONTAINERS_RYUK_DISABLED": "false", + }, + TestcontainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukDisabled: false, + }, + }, { "With Ryuk container privileged using an env var and properties. Env var wins (0)", `ryuk.container.privileged=true`, @@ -230,15 +310,30 @@ func TestReadTCConfig(t *testing.T) { `ryuk.container.privileged=false docker.tls.verify = ERROR`, map[string]string{ + "TESTCONTAINERS_RYUK_DISABLED": "true", "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ Host: "", TLSVerify: 0, CertPath: "", + RyukDisabled: true, RyukPrivileged: true, }, }, + { + "With Ryuk disabled using an env var and properties. Env var does not win because it's not a boolean value", + `ryuk.disabled=false`, + map[string]string{ + "TESTCONTAINERS_RYUK_DISABLED": "foo", + }, + TestcontainersConfig{ + Host: "", + TLSVerify: 0, + CertPath: "", + RyukDisabled: false, + }, + }, { "With Ryuk container privileged using an env var and properties. Env var does not win because it's not a boolean value", `ryuk.container.privileged=false`, From 38e28b71914e76ee0de38ea88c8d04db88bbb705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 18:09:13 +0100 Subject: [PATCH 06/37] chore: rename to doReadConfig --- config.go | 4 ++-- config_test.go | 12 ++++++------ docker.go | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config.go b/config.go index 960fc6ab34..f7f121b0ec 100644 --- a/config.go +++ b/config.go @@ -18,9 +18,9 @@ type TestcontainersConfig struct { RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` } -// readConfig reads from testcontainers properties file, if it exists +// doReadConfig reads from testcontainers properties file, if it exists // it is possible that certain values get overridden when set as environment variables -func readConfig() TestcontainersConfig { +func doReadConfig() TestcontainersConfig { config := TestcontainersConfig{} applyEnvironmentConfiguration := func(config TestcontainersConfig) TestcontainersConfig { diff --git a/config_test.go b/config_test.go index 4aaaaf4e0e..a8fff3a172 100644 --- a/config_test.go +++ b/config_test.go @@ -9,11 +9,11 @@ import ( "github.com/stretchr/testify/assert" ) -func TestReadTCConfig(t *testing.T) { +func TestDoReadTCConfig(t *testing.T) { t.Run("HOME is not set", func(t *testing.T) { t.Setenv("HOME", "") - config := readConfig() + config := doReadConfig() assert.Empty(t, config, "TC props file should not exist") }) @@ -23,7 +23,7 @@ func TestReadTCConfig(t *testing.T) { t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - config := readConfig() + config := doReadConfig() expected := TestcontainersConfig{} expected.RyukDisabled = true @@ -36,7 +36,7 @@ func TestReadTCConfig(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) - config := readConfig() + config := doReadConfig() assert.Empty(t, config, "TC props file should not exist") }) @@ -47,7 +47,7 @@ func TestReadTCConfig(t *testing.T) { t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - config := readConfig() + config := doReadConfig() expected := TestcontainersConfig{} expected.RyukDisabled = true expected.RyukPrivileged = true @@ -360,7 +360,7 @@ func TestReadTCConfig(t *testing.T) { return } - config := readConfig() + config := doReadConfig() assert.Equal(t, tt.expected, config, "Configuration doesn't not match") diff --git a/docker.go b/docker.go index 9d58779982..1707f6a3a2 100644 --- a/docker.go +++ b/docker.go @@ -746,7 +746,7 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) func NewDockerClient() (cli *client.Client, host string, tcConfig TestcontainersConfig, err error) { - tcConfig = readConfig() + tcConfig = doReadConfig() host = tcConfig.Host From d30e16ee43c8e91c9497947f2a48e3c8eefeda40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 18:38:17 +0100 Subject: [PATCH 07/37] chore: read tc config just once --- config.go | 14 ++++++++++++++ config_test.go | 19 +++++++++++++++++++ docker.go | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/config.go b/config.go index f7f121b0ec..364c4ab3b5 100644 --- a/config.go +++ b/config.go @@ -5,10 +5,14 @@ import ( "os" "path/filepath" "strconv" + "sync" "github.com/magiconair/properties" ) +var tcConfig TestcontainersConfig +var tcConfigOnce sync.Once + // TestcontainersConfig represents the configuration for Testcontainers type TestcontainersConfig struct { Host string `properties:"docker.host,default="` @@ -18,6 +22,16 @@ type TestcontainersConfig struct { RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` } +// readConfig reads from testcontainers properties file, storing the result in a singleton instance +// of the TestcontainersConfig struct +func readConfig() TestcontainersConfig { + tcConfigOnce.Do(func() { + tcConfig = doReadConfig() + }) + + return tcConfig +} + // doReadConfig reads from testcontainers properties file, if it exists // it is possible that certain values get overridden when set as environment variables func doReadConfig() TestcontainersConfig { diff --git a/config_test.go b/config_test.go index a8fff3a172..46f604af0c 100644 --- a/config_test.go +++ b/config_test.go @@ -9,6 +9,25 @@ import ( "github.com/stretchr/testify/assert" ) +func TestReadTCConfig(t *testing.T) { + t.Run("Config is read just once", func(t *testing.T) { + t.Setenv("HOME", "") + t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") + + config := readConfig() + + expected := TestcontainersConfig{ + RyukDisabled: true, + } + + assert.Equal(t, expected, config) + + t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "false") + config = readConfig() + assert.Equal(t, expected, config) + }) +} + func TestDoReadTCConfig(t *testing.T) { t.Run("HOME is not set", func(t *testing.T) { t.Setenv("HOME", "") diff --git a/docker.go b/docker.go index 1707f6a3a2..9d58779982 100644 --- a/docker.go +++ b/docker.go @@ -746,7 +746,7 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) func NewDockerClient() (cli *client.Client, host string, tcConfig TestcontainersConfig, err error) { - tcConfig = doReadConfig() + tcConfig = readConfig() host = tcConfig.Host From 1b7e3d636a5fe9e5efe0016212e1ab25f9556335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 19:14:38 +0100 Subject: [PATCH 08/37] chore: deprecate SkipReaper --- container.go | 2 +- docker.go | 15 ++++++----- docker_test.go | 43 +++++++++++++++++--------------- modules/localstack/types_test.go | 3 --- network.go | 2 +- reaper.go | 1 - reaper_test.go | 2 -- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/container.go b/container.go index 35afaa00e4..92ea80999e 100644 --- a/container.go +++ b/container.go @@ -108,7 +108,7 @@ type ContainerRequest struct { Resources container.Resources // Deprecated: Use HostConfigModifier instead Files []ContainerFile // files which will be copied when container starts User string // for specifying uid:gid - SkipReaper bool // indicates whether we skip setting up a reaper for this + SkipReaper bool // Deprecated: The reaper is globally controlled by the .testcontainers.properties file or the TESTCONTAINERS_RYUK_DISABLED environment variable ReaperImage string // Deprecated: use WithImageName ContainerOption instead. Alternative reaper image ReaperOptions []ContainerOption // options for the reaper AutoRemove bool // Deprecated: Use HostConfigModifier instead. If set to true, the container will be removed from the host when stopped diff --git a/docker.go b/docker.go index 9d58779982..87b33e67b9 100644 --- a/docker.go +++ b/docker.go @@ -67,7 +67,6 @@ type DockerContainer struct { provider *DockerProvider sessionID uuid.UUID terminationSignal chan bool - skipReaper bool consumers []LogConsumer raw *types.ContainerJSON stopProducer chan bool @@ -902,10 +901,12 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque opt(&reaperOpts) } + tcConfig := p.Config() + var termSignal chan bool // the reaper does not need to start a reaper for itself isReaperContainer := strings.EqualFold(req.Image, reaperImage(reaperOpts.ImageName)) - if !req.SkipReaper && !isReaperContainer { + if !tcConfig.RyukDisabled && !isReaperContainer { r, err := reuseOrCreateReaper(context.WithValue(ctx, testcontainersdocker.DockerHostContextKey, p.host), testcontainerssession.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating reaper failed", err) @@ -1042,7 +1043,6 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque sessionID: testcontainerssession.ID(), provider: p, terminationSignal: termSignal, - skipReaper: req.SkipReaper, stopProducer: make(chan bool), logger: p.Logger, } @@ -1085,8 +1085,10 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain return p.CreateContainer(ctx, req) } + tcConfig := p.Config() + var termSignal chan bool - if !req.SkipReaper { + if !tcConfig.RyukDisabled { r, err := reuseOrCreateReaper(context.WithValue(ctx, testcontainersdocker.DockerHostContextKey, p.host), testcontainerssession.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating reaper failed", err) @@ -1105,7 +1107,6 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain sessionID: testcontainerssession.ID(), provider: p, terminationSignal: termSignal, - skipReaper: req.SkipReaper, stopProducer: make(chan bool), logger: p.Logger, isRunning: c.State == "running", @@ -1240,6 +1241,8 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) req.Labels = make(map[string]string) } + tcConfig := p.Config() + nc := types.NetworkCreate{ Driver: req.Driver, CheckDuplicate: req.CheckDuplicate, @@ -1251,7 +1254,7 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) } var termSignal chan bool - if !req.SkipReaper { + if !tcConfig.RyukDisabled { r, err := reuseOrCreateReaper(context.WithValue(ctx, testcontainersdocker.DockerHostContextKey, p.host), testcontainerssession.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating network reaper failed", err) diff --git a/docker_test.go b/docker_test.go index 612d3267aa..36ec810de1 100644 --- a/docker_test.go +++ b/docker_test.go @@ -140,9 +140,8 @@ func TestContainerWithHostNetworkOptions(t *testing.T) { gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxAlpineImage, - SkipReaper: true, - Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), + Image: nginxAlpineImage, + Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), ExposedPorts: []string{ nginxHighPort, }, @@ -181,7 +180,6 @@ func TestContainerWithHostNetworkOptions_UseExposePortsFromImageConfigs(t *testi ContainerRequest: ContainerRequest{ Image: "nginx", Privileged: true, - SkipReaper: true, WaitingFor: wait.ForExposedPort(), }, Started: true, @@ -210,9 +208,8 @@ func TestContainerWithNetworkModeAndNetworkTogether(t *testing.T) { gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxImage, - SkipReaper: true, - Networks: []string{"new-network"}, + Image: nginxImage, + Networks: []string{"new-network"}, HostConfigModifier: func(hc *container.HostConfig) { hc.NetworkMode = "host" }, @@ -240,7 +237,6 @@ func TestContainerWithHostNetworkOptionsAndWaitStrategy(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxAlpineImage, - SkipReaper: true, WaitingFor: wait.ForListeningPort(nginxHighPort), Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), HostConfigModifier: func(hc *container.HostConfig) { @@ -278,7 +274,6 @@ func TestContainerWithHostNetworkAndEndpoint(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxAlpineImage, - SkipReaper: true, WaitingFor: wait.ForListeningPort(nginxHighPort), Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), HostConfigModifier: func(hc *container.HostConfig) { @@ -317,7 +312,6 @@ func TestContainerWithHostNetworkAndPortEndpoint(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxAlpineImage, - SkipReaper: true, WaitingFor: wait.ForListeningPort(nginxHighPort), Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), HostConfigModifier: func(hc *container.HostConfig) { @@ -365,6 +359,11 @@ func TestContainerReturnItsContainerID(t *testing.T) { } func TestContainerStartsWithoutTheReaper(t *testing.T) { + tcConfig := readConfig() + if !tcConfig.RyukDisabled { + t.Skip("Ryuk is enabled, skipping test") + } + ctx := context.Background() client, err := testcontainersdocker.NewClient(ctx) if err != nil { @@ -380,7 +379,6 @@ func TestContainerStartsWithoutTheReaper(t *testing.T) { ExposedPorts: []string{ nginxDefaultPort, }, - SkipReaper: true, }, Started: true, }) @@ -437,6 +435,11 @@ func TestContainerStartsWithTheReaper(t *testing.T) { } func TestContainerTerminationResetsState(t *testing.T) { + tcConfig := readConfig() + if !tcConfig.RyukDisabled { + t.Skip("Ryuk is enabled, skipping test") + } + ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ @@ -446,7 +449,6 @@ func TestContainerTerminationResetsState(t *testing.T) { ExposedPorts: []string{ nginxDefaultPort, }, - SkipReaper: true, }, Started: true, }) @@ -602,6 +604,11 @@ func TestContainerTerminationWithReaper(t *testing.T) { } func TestContainerTerminationWithoutReaper(t *testing.T) { + tcConfig := readConfig() + if !tcConfig.RyukDisabled { + t.Skip("Ryuk is enabled, skipping test") + } + ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ @@ -611,7 +618,6 @@ func TestContainerTerminationWithoutReaper(t *testing.T) { ExposedPorts: []string{ nginxDefaultPort, }, - SkipReaper: true, }, Started: true, }) @@ -653,7 +659,6 @@ func TestContainerTerminationRemovesDockerImage(t *testing.T) { ExposedPorts: []string{ nginxDefaultPort, }, - SkipReaper: true, }, Started: true, }) @@ -1473,8 +1478,7 @@ 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{ - Image: "postgres:nonexistent-version", - SkipReaper: true, + Image: "postgres:nonexistent-version", }, Started: true, }) @@ -1488,18 +1492,19 @@ 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(), time.Second) defer cancel() - _, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: "docker.io/postgres:12", WaitingFor: wait.ForLog("log"), - SkipReaper: true, }, Started: true, }) if !errors.Is(err, ctx.Err()) { t.Fatalf("err should be a ctx cancelled error %v", err) } + + terminateContainerOnEnd(t, ctx, c) }) } @@ -1516,7 +1521,6 @@ func TestContainerCustomPlatformImage(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: "docker.io/redis:latest", - SkipReaper: true, ImagePlatform: nonExistentPlatform, }, Started: false, @@ -1535,7 +1539,6 @@ func TestContainerCustomPlatformImage(t *testing.T) { ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: "docker.io/mysql:5.7", - SkipReaper: true, ImagePlatform: "linux/amd64", }, Started: false, diff --git a/modules/localstack/types_test.go b/modules/localstack/types_test.go index d5eb3cf283..faae146e51 100644 --- a/modules/localstack/types_test.go +++ b/modules/localstack/types_test.go @@ -14,7 +14,6 @@ func TestOverrideContainerRequest(t *testing.T) { Env: map[string]string{}, Image: "foo", ExposedPorts: []string{}, - SkipReaper: false, WaitingFor: wait.ForNop( func(ctx context.Context, target wait.StrategyTarget) error { return nil @@ -37,11 +36,9 @@ func TestOverrideContainerRequest(t *testing.T) { "foo1": {"bar"}, }, WaitingFor: wait.ForLog("foo"), - SkipReaper: true, })(req) assert.Equal(t, "BAR", merged.Env["FOO"]) - assert.True(t, merged.SkipReaper) assert.Equal(t, "bar", merged.Image) assert.Equal(t, []string{"12345/tcp"}, merged.ExposedPorts) assert.Equal(t, []string{"foo1", "bar1"}, merged.Networks) diff --git a/network.go b/network.go index e8436240fc..841347f11b 100644 --- a/network.go +++ b/network.go @@ -40,7 +40,7 @@ type NetworkRequest struct { Attachable bool IPAM *network.IPAM - SkipReaper bool // indicates whether we skip setting up a reaper for this + SkipReaper bool // Deprecated: The reaper is globally controlled by the .testcontainers.properties file or the TESTCONTAINERS_RYUK_DISABLED environment variable ReaperImage string // Deprecated: use WithImageName ContainerOption instead. Alternative reaper registry ReaperOptions []ContainerOption // Reaper options to use for this network } diff --git a/reaper.go b/reaper.go index 9ee6f293d6..bfb91cbdb1 100644 --- a/reaper.go +++ b/reaper.go @@ -97,7 +97,6 @@ func newReaper(ctx context.Context, sessionID string, provider ReaperProvider, o TestcontainerLabelIsReaper: "true", testcontainersdocker.LabelReaper: "true", }, - SkipReaper: true, Mounts: Mounts(BindMount(dockerHost, "/var/run/docker.sock")), Privileged: tcConfig.RyukPrivileged, WaitingFor: wait.ForListeningPort(listeningPort), diff --git a/reaper_test.go b/reaper_test.go index 17f7258fc3..36e9d4ebef 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -60,7 +60,6 @@ func createContainerRequest(customize func(ContainerRequest) ContainerRequest) C testcontainersdocker.LabelLang: "go", testcontainersdocker.LabelVersion: internal.Version, }, - SkipReaper: true, Mounts: Mounts(BindMount("/var/run/docker.sock", "/var/run/docker.sock")), WaitingFor: wait.ForListeningPort(nat.Port("8080/tcp")), ReaperOptions: []ContainerOption{ @@ -126,7 +125,6 @@ func Test_NewReaper(t *testing.T) { assert.Equal(t, test.req.Image, provider.req.Image, "expected image doesn't match the submitted request") assert.Equal(t, test.req.ExposedPorts, provider.req.ExposedPorts, "expected exposed ports don't match the submitted request") assert.Equal(t, test.req.Labels, provider.req.Labels, "expected labels don't match the submitted request") - assert.Equal(t, test.req.SkipReaper, provider.req.SkipReaper, "expected skipReaper doesn't match the submitted request") assert.Equal(t, test.req.Mounts, provider.req.Mounts, "expected mounts don't match the submitted request") assert.Equal(t, test.req.WaitingFor, provider.req.WaitingFor, "expected waitingFor don't match the submitted request") From 7704d76fa03cf7d24c983b7c1b2bed3de6016d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 19:17:08 +0100 Subject: [PATCH 09/37] chore: add a pipeline for tests without Ryuk --- .github/workflows/ci-reaper-off.yml | 69 +++++++++++++++++++++++++++++ .github/workflows/ci.yml | 6 +++ 2 files changed, 75 insertions(+) create mode 100644 .github/workflows/ci-reaper-off.yml diff --git a/.github/workflows/ci-reaper-off.yml b/.github/workflows/ci-reaper-off.yml new file mode 100644 index 0000000000..f4321fec08 --- /dev/null +++ b/.github/workflows/ci-reaper-off.yml @@ -0,0 +1,69 @@ +name: Reaper-Off pipeline + +on: [push, pull_request] + +concurrency: + group: "${{ github.workflow }}-${{ github.head_ref || github.sha }}" + cancel-in-progress: true + +jobs: + static-analysis: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v3 + - name: Run ShellCheck + run: | + shellcheck scripts/*.sh + - name: Run gofmt + run: | + ./scripts/checks.sh + test: + strategy: + matrix: + go-version: [1.19.x, 1.x] + runs-on: ubuntu-latest + steps: + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go-version }} + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + + - name: modVerify + run: go mod verify + + - name: modTidy + run: make tools-tidy + + - name: vet + run: go vet ./... + + - name: ensure compilation + env: + GOOS: linux + run: go build + + - name: Disable Ryuk at properties side + run: | + set -x + echo 'ryuk.disabled=true' >> ${HOME}/.testcontainers.properties + - name: gotestsum + # only run tests on linux, there are a number of things that won't allow the tests to run on anything else + # many (maybe, all?) images used can only be build on Linux, they don't have Windows in their manifest, and + # we can't put Windows Server in "Linux Mode" in Github actions + # another, host mode is only available on Linux, and we have tests around that, do we skip them? + run: make test-unit + + - name: Run checker + run: | + ./scripts/check_environment.sh + - name: Test Summary + uses: test-summary/action@4ee9ece4bca777a38f05c8fc578ac2007fe266f7 + with: + paths: "**/TEST-*.xml" + if: always() diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7bb1a304c8..7e745acb7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,12 @@ jobs: GOOS: linux run: go build + - name: Enable Ryuk at properties side + if: ${{ matrix.platform == 'ubuntu-latest' }} + run: | + set -x + echo 'ryuk.disabled=false' >> ${HOME}/.testcontainers.properties + - name: gotestsum # only run tests on linux, there are a number of things that won't allow the tests to run on anything else # many (maybe, all?) images used can only be build on Linux, they don't have Windows in their manifest, and From cbdb265779109d82d2c53a3d2398e83012f52441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 19:17:18 +0100 Subject: [PATCH 10/37] docs: document Ryuk --- docs/features/garbage_collector.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/features/garbage_collector.md b/docs/features/garbage_collector.md index 643d6ca46d..e725f5a9bd 100644 --- a/docs/features/garbage_collector.md +++ b/docs/features/garbage_collector.md @@ -39,8 +39,10 @@ for more than 10 seconds, it will be killed. !!!warning - This feature can be disabled when creating a container or a network, - but it can cause **unexpected behavior** in your environment. + This feature can be disabled in two different manners, but it can cause **unexpected behavior** in your environment: + + 1. adding `ryuk.disabled=true` to the `.testcontainers.properties` file. + 2. setting the `TESTCONTAINERS_RYUK_DISABLED=true` environment variable. This manner takes precedence over the properties file. We recommend using it only for Continuous Integration services that have their own mechanism to clean up resources. From 76e6986e5278256b362cd18c6d47947bca435826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 14 Mar 2023 19:19:03 +0100 Subject: [PATCH 11/37] chore: use same message when disabling Ryuk --- docker.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docker.go b/docker.go index 87b33e67b9..ae67939d99 100644 --- a/docker.go +++ b/docker.go @@ -921,7 +921,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque } } } else if !isReaperContainer { - p.printReaperBanner("container") + p.printReaperBanner() } if err = req.Validate(); err != nil { @@ -1098,7 +1098,7 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain return nil, fmt.Errorf("%w: connecting to reaper failed", err) } } else { - p.printReaperBanner("container") + p.printReaperBanner() } dc := &DockerContainer{ ID: c.ID, @@ -1269,7 +1269,7 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) } } } else { - p.printReaperBanner("network") + p.printReaperBanner() } response, err := p.client.NetworkCreate(ctx, req.Name, nc) @@ -1328,12 +1328,12 @@ func (p *DockerProvider) GetGatewayIP(ctx context.Context) (string, error) { return ip, nil } -func (p *DockerProvider) printReaperBanner(resource string) { +func (p *DockerProvider) printReaperBanner() { ryukDisabledMessage := ` - ********************************************************************************************** - Ryuk has been disabled for the ` + resource + `. This can cause unexpected behavior in your environment. - More on this: https://golang.testcontainers.org/features/garbage_collector/ - **********************************************************************************************` +********************************************************************************************** +Ryuk has been disabled for the current execution. This can cause unexpected behavior in your environment. +More on this: https://golang.testcontainers.org/features/garbage_collector/ +**********************************************************************************************` p.Logger.Printf(ryukDisabledMessage) } From 1935912e241fa7f3a8cc5b03bd8cb64d857cc3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 07:37:43 +0100 Subject: [PATCH 12/37] chore: print reaper banner when reading properties for first time --- config.go | 9 +++++++++ docker.go | 16 +--------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/config.go b/config.go index 364c4ab3b5..c68e0b0a13 100644 --- a/config.go +++ b/config.go @@ -27,6 +27,15 @@ type TestcontainersConfig struct { func readConfig() TestcontainersConfig { tcConfigOnce.Do(func() { tcConfig = doReadConfig() + + if tcConfig.RyukDisabled { + ryukDisabledMessage := ` +********************************************************************************************** +Ryuk has been disabled for the current execution. This can cause unexpected behavior in your environment. +More on this: https://golang.testcontainers.org/features/garbage_collector/ +**********************************************************************************************` + Logger.Printf(ryukDisabledMessage) + } }) return tcConfig diff --git a/docker.go b/docker.go index ae67939d99..e4b9a54b2b 100644 --- a/docker.go +++ b/docker.go @@ -920,8 +920,6 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque req.Labels[k] = v } } - } else if !isReaperContainer { - p.printReaperBanner() } if err = req.Validate(); err != nil { @@ -1097,9 +1095,8 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain if err != nil { return nil, fmt.Errorf("%w: connecting to reaper failed", err) } - } else { - p.printReaperBanner() } + dc := &DockerContainer{ ID: c.ID, WaitingFor: req.WaitingFor, @@ -1268,8 +1265,6 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) req.Labels[k] = v } } - } else { - p.printReaperBanner() } response, err := p.client.NetworkCreate(ctx, req.Name, nc) @@ -1328,15 +1323,6 @@ func (p *DockerProvider) GetGatewayIP(ctx context.Context) (string, error) { return ip, nil } -func (p *DockerProvider) printReaperBanner() { - ryukDisabledMessage := ` -********************************************************************************************** -Ryuk has been disabled for the current execution. This can cause unexpected behavior in your environment. -More on this: https://golang.testcontainers.org/features/garbage_collector/ -**********************************************************************************************` - p.Logger.Printf(ryukDisabledMessage) -} - func (p *DockerProvider) getDefaultNetwork(ctx context.Context, cli client.APIClient) (string, error) { // Get list of available networks networkResources, err := cli.NetworkList(ctx, types.NetworkListOptions{}) From e01ad917ea8d8e6d514f7fc30cf1cca5704d8c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 07:41:12 +0100 Subject: [PATCH 13/37] chore: expose reading TC config --- config.go | 10 +++++----- config_test.go | 18 +++++++++--------- docker.go | 2 +- docker_test.go | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/config.go b/config.go index c68e0b0a13..b3e2e21963 100644 --- a/config.go +++ b/config.go @@ -22,11 +22,11 @@ type TestcontainersConfig struct { RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` } -// readConfig reads from testcontainers properties file, storing the result in a singleton instance +// ReadConfig reads from testcontainers properties file, storing the result in a singleton instance // of the TestcontainersConfig struct -func readConfig() TestcontainersConfig { +func ReadConfig() TestcontainersConfig { tcConfigOnce.Do(func() { - tcConfig = doReadConfig() + tcConfig = readConfig() if tcConfig.RyukDisabled { ryukDisabledMessage := ` @@ -41,9 +41,9 @@ More on this: https://golang.testcontainers.org/features/garbage_collector/ return tcConfig } -// doReadConfig reads from testcontainers properties file, if it exists +// readConfig reads from testcontainers properties file, if it exists // it is possible that certain values get overridden when set as environment variables -func doReadConfig() TestcontainersConfig { +func readConfig() TestcontainersConfig { config := TestcontainersConfig{} applyEnvironmentConfiguration := func(config TestcontainersConfig) TestcontainersConfig { diff --git a/config_test.go b/config_test.go index 46f604af0c..5b834b9ab0 100644 --- a/config_test.go +++ b/config_test.go @@ -9,12 +9,12 @@ import ( "github.com/stretchr/testify/assert" ) -func TestReadTCConfig(t *testing.T) { +func TestReadConfig(t *testing.T) { t.Run("Config is read just once", func(t *testing.T) { t.Setenv("HOME", "") t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") - config := readConfig() + config := ReadConfig() expected := TestcontainersConfig{ RyukDisabled: true, @@ -23,16 +23,16 @@ func TestReadTCConfig(t *testing.T) { assert.Equal(t, expected, config) t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "false") - config = readConfig() + config = ReadConfig() assert.Equal(t, expected, config) }) } -func TestDoReadTCConfig(t *testing.T) { +func TestReadTCConfig(t *testing.T) { t.Run("HOME is not set", func(t *testing.T) { t.Setenv("HOME", "") - config := doReadConfig() + config := readConfig() assert.Empty(t, config, "TC props file should not exist") }) @@ -42,7 +42,7 @@ func TestDoReadTCConfig(t *testing.T) { t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - config := doReadConfig() + config := readConfig() expected := TestcontainersConfig{} expected.RyukDisabled = true @@ -55,7 +55,7 @@ func TestDoReadTCConfig(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) - config := doReadConfig() + config := readConfig() assert.Empty(t, config, "TC props file should not exist") }) @@ -66,7 +66,7 @@ func TestDoReadTCConfig(t *testing.T) { t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "true") - config := doReadConfig() + config := readConfig() expected := TestcontainersConfig{} expected.RyukDisabled = true expected.RyukPrivileged = true @@ -379,7 +379,7 @@ func TestDoReadTCConfig(t *testing.T) { return } - config := doReadConfig() + config := readConfig() assert.Equal(t, tt.expected, config, "Configuration doesn't not match") diff --git a/docker.go b/docker.go index e4b9a54b2b..a426ce0a65 100644 --- a/docker.go +++ b/docker.go @@ -745,7 +745,7 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) func NewDockerClient() (cli *client.Client, host string, tcConfig TestcontainersConfig, err error) { - tcConfig = readConfig() + tcConfig = ReadConfig() host = tcConfig.Host diff --git a/docker_test.go b/docker_test.go index 36ec810de1..5f942cdbb9 100644 --- a/docker_test.go +++ b/docker_test.go @@ -359,7 +359,7 @@ func TestContainerReturnItsContainerID(t *testing.T) { } func TestContainerStartsWithoutTheReaper(t *testing.T) { - tcConfig := readConfig() + tcConfig := ReadConfig() if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") } @@ -435,7 +435,7 @@ func TestContainerStartsWithTheReaper(t *testing.T) { } func TestContainerTerminationResetsState(t *testing.T) { - tcConfig := readConfig() + tcConfig := ReadConfig() if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") } @@ -604,7 +604,7 @@ func TestContainerTerminationWithReaper(t *testing.T) { } func TestContainerTerminationWithoutReaper(t *testing.T) { - tcConfig := readConfig() + tcConfig := ReadConfig() if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") } From 9f27165fedc54c0dd3d475baf0104d5a1e6f4a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 07:44:03 +0100 Subject: [PATCH 14/37] break: do not return the config when retrieving a Docker client --- docker.go | 8 ++++---- docker_test.go | 6 +++--- modules/compose/compose_api.go | 2 +- modules/compose/compose_test.go | 4 ++-- provider.go | 4 +++- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/docker.go b/docker.go index a426ce0a65..0ab6f49ed5 100644 --- a/docker.go +++ b/docker.go @@ -744,7 +744,7 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) -func NewDockerClient() (cli *client.Client, host string, tcConfig TestcontainersConfig, err error) { +func NewDockerClient() (cli *client.Client, host string, err error) { tcConfig = ReadConfig() host = tcConfig.Host @@ -775,7 +775,7 @@ func NewDockerClient() (cli *client.Client, host string, tcConfig Testcontainers cli, err = client.NewClientWithOpts(opts...) if err != nil { - return nil, "", TestcontainersConfig{}, err + return nil, "", err } _, err = cli.Ping(context.TODO()) @@ -783,12 +783,12 @@ func NewDockerClient() (cli *client.Client, host string, tcConfig Testcontainers // fallback to environment cli, err = testcontainersdocker.NewClient(context.Background()) if err != nil { - return nil, "", TestcontainersConfig{}, err + return nil, "", err } } defer cli.Close() - return cli, host, tcConfig, nil + return cli, host, nil } // BuildImage will build and image from context and Dockerfile, then return the tag diff --git a/docker_test.go b/docker_test.go index 5f942cdbb9..a5256d6600 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1393,7 +1393,7 @@ func TestContainerCreationWithBindAndVolume(t *testing.T) { ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second) defer cnl() // Create a Docker client. - dockerCli, _, _, err := NewDockerClient() + dockerCli, _, err := NewDockerClient() if err != nil { t.Fatal(err) } @@ -1547,7 +1547,7 @@ func TestContainerCustomPlatformImage(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, c) - dockerCli, _, _, err := NewDockerClient() + dockerCli, _, err := NewDockerClient() require.NoError(t, err) ctr, err := dockerCli.ContainerInspect(ctx, c.GetContainerID()) @@ -1584,7 +1584,7 @@ func TestContainerWithCustomHostname(t *testing.T) { } func readHostname(tb testing.TB, containerId string) string { - containerClient, _, _, err := NewDockerClient() + containerClient, _, err := NewDockerClient() if err != nil { tb.Fatalf("Failed to create Docker client: %v", err) } diff --git a/modules/compose/compose_api.go b/modules/compose/compose_api.go index c6e23b50a4..e27c3c7f09 100644 --- a/modules/compose/compose_api.go +++ b/modules/compose/compose_api.go @@ -371,7 +371,7 @@ func withEnv(env map[string]string) func(*cli.ProjectOptions) error { } func makeClient(*command.DockerCli) (client.APIClient, error) { - dockerClient, _, _, err := testcontainers.NewDockerClient() + dockerClient, _, err := testcontainers.NewDockerClient() if err != nil { return nil, err } diff --git a/modules/compose/compose_test.go b/modules/compose/compose_test.go index bf243885e6..b42fcf4b2d 100644 --- a/modules/compose/compose_test.go +++ b/modules/compose/compose_test.go @@ -445,7 +445,7 @@ func TestLocalDockerComposeWithVolume(t *testing.T) { } func assertVolumeDoesNotExist(tb testing.TB, volumeName string) { - containerClient, _, _, err := testcontainers.NewDockerClient() + containerClient, _, err := testcontainers.NewDockerClient() if err != nil { tb.Fatalf("Failed to get provider: %v", err) } @@ -470,7 +470,7 @@ func assertContainerEnvironmentVariables( present map[string]string, absent map[string]string, ) { - containerClient, _, _, err := testcontainers.NewDockerClient() + containerClient, _, err := testcontainers.NewDockerClient() if err != nil { tb.Fatalf("Failed to get provider: %v", err) } diff --git a/provider.go b/provider.go index 2f3231556a..70a3a369ae 100644 --- a/provider.go +++ b/provider.go @@ -128,11 +128,13 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error provOpts[idx].ApplyDockerTo(o) } - c, host, tcConfig, err := NewDockerClient() + c, host, err := NewDockerClient() if err != nil { return nil, err } + tcConfig := ReadConfig() + p := &DockerProvider{ DockerProviderOptions: o, host: host, From e754c24ea88ae5770e4948426f52974cee0d46f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 07:55:19 +0100 Subject: [PATCH 15/37] break: do not return the Docker Host when retrieving a Docker client It comes in the TC config --- config.go | 8 +++++++- config_test.go | 13 +++++++++++++ docker.go | 14 +++++--------- docker_test.go | 6 +++--- modules/compose/compose_api.go | 2 +- modules/compose/compose_test.go | 4 ++-- provider.go | 4 ++-- 7 files changed, 33 insertions(+), 18 deletions(-) diff --git a/config.go b/config.go index b3e2e21963..f99a359aa3 100644 --- a/config.go +++ b/config.go @@ -44,9 +44,15 @@ More on this: https://golang.testcontainers.org/features/garbage_collector/ // readConfig reads from testcontainers properties file, if it exists // it is possible that certain values get overridden when set as environment variables func readConfig() TestcontainersConfig { - config := TestcontainersConfig{} + config := TestcontainersConfig{ + Host: "unix:///var/run/docker.sock", + } applyEnvironmentConfiguration := func(config TestcontainersConfig) TestcontainersConfig { + if dockerHostEnv := os.Getenv("DOCKER_HOST"); dockerHostEnv != "" { + config.Host = dockerHostEnv + } + ryukDisabledEnv := os.Getenv("TESTCONTAINERS_RYUK_DISABLED") if parseBool(ryukDisabledEnv) { config.RyukDisabled = ryukDisabledEnv == "true" diff --git a/config_test.go b/config_test.go index 5b834b9ab0..b7b5d4e254 100644 --- a/config_test.go +++ b/config_test.go @@ -18,6 +18,7 @@ func TestReadConfig(t *testing.T) { expected := TestcontainersConfig{ RyukDisabled: true, + Host: "unix:///var/run/docker.sock", } assert.Equal(t, expected, config) @@ -60,6 +61,18 @@ func TestReadTCConfig(t *testing.T) { assert.Empty(t, config, "TC props file should not exist") }) + t.Run("HOME does not contain TC props file - DOCKER_HOST env is set", func(t *testing.T) { + tmpDir := t.TempDir() + t.Setenv("HOME", tmpDir) + t.Setenv("DOCKER_HOST", "tcp://127.0.0.1:33293") + + config := readConfig() + expected := TestcontainersConfig{} + expected.Host = "tcp://127.0.0.1:33293" + + assert.Equal(t, expected, config) + }) + t.Run("HOME does not contain TC props file - TESTCONTAINERS_ env is set", func(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) diff --git a/docker.go b/docker.go index 0ab6f49ed5..48edb8782f 100644 --- a/docker.go +++ b/docker.go @@ -744,10 +744,10 @@ func (p *DockerProvider) SetClient(c client.APIClient) { var _ ContainerProvider = (*DockerProvider)(nil) -func NewDockerClient() (cli *client.Client, host string, err error) { +func NewDockerClient() (cli *client.Client, err error) { tcConfig = ReadConfig() - host = tcConfig.Host + host := tcConfig.Host opts := []client.Opt{client.FromEnv, client.WithAPIVersionNegotiation()} if host != "" { @@ -761,10 +761,6 @@ func NewDockerClient() (cli *client.Client, host string, err error) { opts = append(opts, client.WithTLSClientConfig(cacertPath, certPath, keyPath)) } - } else if dockerHostEnv := os.Getenv("DOCKER_HOST"); dockerHostEnv != "" { - host = dockerHostEnv - } else { - host = "unix:///var/run/docker.sock" } opts = append(opts, client.WithHTTPHeaders( @@ -775,7 +771,7 @@ func NewDockerClient() (cli *client.Client, host string, err error) { cli, err = client.NewClientWithOpts(opts...) if err != nil { - return nil, "", err + return nil, err } _, err = cli.Ping(context.TODO()) @@ -783,12 +779,12 @@ func NewDockerClient() (cli *client.Client, host string, err error) { // fallback to environment cli, err = testcontainersdocker.NewClient(context.Background()) if err != nil { - return nil, "", err + return nil, err } } defer cli.Close() - return cli, host, nil + return cli, nil } // BuildImage will build and image from context and Dockerfile, then return the tag diff --git a/docker_test.go b/docker_test.go index a5256d6600..1d89157215 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1393,7 +1393,7 @@ func TestContainerCreationWithBindAndVolume(t *testing.T) { ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second) defer cnl() // Create a Docker client. - dockerCli, _, err := NewDockerClient() + dockerCli, err := NewDockerClient() if err != nil { t.Fatal(err) } @@ -1547,7 +1547,7 @@ func TestContainerCustomPlatformImage(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, c) - dockerCli, _, err := NewDockerClient() + dockerCli, err := NewDockerClient() require.NoError(t, err) ctr, err := dockerCli.ContainerInspect(ctx, c.GetContainerID()) @@ -1584,7 +1584,7 @@ func TestContainerWithCustomHostname(t *testing.T) { } func readHostname(tb testing.TB, containerId string) string { - containerClient, _, err := NewDockerClient() + containerClient, err := NewDockerClient() if err != nil { tb.Fatalf("Failed to create Docker client: %v", err) } diff --git a/modules/compose/compose_api.go b/modules/compose/compose_api.go index e27c3c7f09..5fb3c8a453 100644 --- a/modules/compose/compose_api.go +++ b/modules/compose/compose_api.go @@ -371,7 +371,7 @@ func withEnv(env map[string]string) func(*cli.ProjectOptions) error { } func makeClient(*command.DockerCli) (client.APIClient, error) { - dockerClient, _, err := testcontainers.NewDockerClient() + dockerClient, err := testcontainers.NewDockerClient() if err != nil { return nil, err } diff --git a/modules/compose/compose_test.go b/modules/compose/compose_test.go index b42fcf4b2d..6daa8387d6 100644 --- a/modules/compose/compose_test.go +++ b/modules/compose/compose_test.go @@ -445,7 +445,7 @@ func TestLocalDockerComposeWithVolume(t *testing.T) { } func assertVolumeDoesNotExist(tb testing.TB, volumeName string) { - containerClient, _, err := testcontainers.NewDockerClient() + containerClient, err := testcontainers.NewDockerClient() if err != nil { tb.Fatalf("Failed to get provider: %v", err) } @@ -470,7 +470,7 @@ func assertContainerEnvironmentVariables( present map[string]string, absent map[string]string, ) { - containerClient, _, err := testcontainers.NewDockerClient() + containerClient, err := testcontainers.NewDockerClient() if err != nil { tb.Fatalf("Failed to get provider: %v", err) } diff --git a/provider.go b/provider.go index 70a3a369ae..c16d449e50 100644 --- a/provider.go +++ b/provider.go @@ -128,7 +128,7 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error provOpts[idx].ApplyDockerTo(o) } - c, host, err := NewDockerClient() + c, err := NewDockerClient() if err != nil { return nil, err } @@ -137,7 +137,7 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error p := &DockerProvider{ DockerProviderOptions: o, - host: host, + host: tcConfig.Host, client: c, config: tcConfig, } From 068974cf1c2f577e58210ce5e7edbea5fbe2ba92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 10:07:30 +0100 Subject: [PATCH 16/37] chore: simplify reaper-off pipeline --- .github/workflows/ci-reaper-off.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/ci-reaper-off.yml b/.github/workflows/ci-reaper-off.yml index f4321fec08..e9d0f7f58d 100644 --- a/.github/workflows/ci-reaper-off.yml +++ b/.github/workflows/ci-reaper-off.yml @@ -34,19 +34,6 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v3 - - name: modVerify - run: go mod verify - - - name: modTidy - run: make tools-tidy - - - name: vet - run: go vet ./... - - - name: ensure compilation - env: - GOOS: linux - run: go build - name: Disable Ryuk at properties side run: | @@ -62,6 +49,7 @@ jobs: - name: Run checker run: | ./scripts/check_environment.sh + - name: Test Summary uses: test-summary/action@4ee9ece4bca777a38f05c8fc578ac2007fe266f7 with: From bfaafe721a34b68eff3f08896e3d686a766f013e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 10:23:51 +0100 Subject: [PATCH 17/37] chore: skip reaper test when it's disabled --- docker_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker_test.go b/docker_test.go index 1d89157215..5ac3cce53c 100644 --- a/docker_test.go +++ b/docker_test.go @@ -398,6 +398,11 @@ func TestContainerStartsWithoutTheReaper(t *testing.T) { } func TestContainerStartsWithTheReaper(t *testing.T) { + tcConfig := ReadConfig() + if tcConfig.RyukDisabled { + t.Skip("Ryuk is disabled, skipping test") + } + ctx := context.Background() client, err := testcontainersdocker.NewClient(ctx) if err != nil { From 953287288bb9002b7c9da9fb26c4e7e4a5c96b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 10:47:58 +0100 Subject: [PATCH 18/37] fix: update tests --- config_test.go | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/config_test.go b/config_test.go index b7b5d4e254..828969f00c 100644 --- a/config_test.go +++ b/config_test.go @@ -35,7 +35,10 @@ func TestReadTCConfig(t *testing.T) { config := readConfig() - assert.Empty(t, config, "TC props file should not exist") + expected := TestcontainersConfig{} + expected.Host = "unix:///var/run/docker.sock" + + assert.Equal(t, expected, config) }) t.Run("HOME is not set - TESTCONTAINERS_ env is set", func(t *testing.T) { @@ -48,6 +51,7 @@ func TestReadTCConfig(t *testing.T) { expected := TestcontainersConfig{} expected.RyukDisabled = true expected.RyukPrivileged = true + expected.Host = "unix:///var/run/docker.sock" assert.Equal(t, expected, config) }) @@ -58,7 +62,10 @@ func TestReadTCConfig(t *testing.T) { config := readConfig() - assert.Empty(t, config, "TC props file should not exist") + expected := TestcontainersConfig{} + expected.Host = "unix:///var/run/docker.sock" + + assert.Equal(t, expected, config) }) t.Run("HOME does not contain TC props file - DOCKER_HOST env is set", func(t *testing.T) { @@ -83,6 +90,7 @@ func TestReadTCConfig(t *testing.T) { expected := TestcontainersConfig{} expected.RyukDisabled = true expected.RyukPrivileged = true + expected.Host = "unix:///var/run/docker.sock" assert.Equal(t, expected, config) }) @@ -135,7 +143,7 @@ func TestReadTCConfig(t *testing.T) { "", map[string]string{}, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", }, @@ -167,7 +175,7 @@ func TestReadTCConfig(t *testing.T) { `#docker.host=tcp://127.0.0.1:33293`, map[string]string{}, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", }, @@ -190,7 +198,7 @@ func TestReadTCConfig(t *testing.T) { `ryuk.disabled=true`, map[string]string{}, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -201,7 +209,7 @@ func TestReadTCConfig(t *testing.T) { `ryuk.container.privileged=true`, map[string]string{}, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -214,7 +222,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "true", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -227,7 +235,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -240,7 +248,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "true", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -253,7 +261,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "true", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -266,7 +274,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "false", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: false, @@ -279,7 +287,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "false", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: false, @@ -292,7 +300,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -305,7 +313,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -318,7 +326,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukPrivileged: false, @@ -331,7 +339,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukPrivileged: false, @@ -346,7 +354,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -360,7 +368,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "foo", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukDisabled: false, @@ -373,7 +381,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "foo", }, TestcontainersConfig{ - Host: "", + Host: "unix:///var/run/docker.sock", TLSVerify: 0, CertPath: "", RyukPrivileged: false, From 560672ac241cb3a96a9dcb1d0124c6fec8b11045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 10:49:50 +0100 Subject: [PATCH 19/37] fix: default for docker host --- config.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/config.go b/config.go index f99a359aa3..7567ccaa32 100644 --- a/config.go +++ b/config.go @@ -44,14 +44,15 @@ More on this: https://golang.testcontainers.org/features/garbage_collector/ // readConfig reads from testcontainers properties file, if it exists // it is possible that certain values get overridden when set as environment variables func readConfig() TestcontainersConfig { - config := TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", - } + config := TestcontainersConfig{} applyEnvironmentConfiguration := func(config TestcontainersConfig) TestcontainersConfig { if dockerHostEnv := os.Getenv("DOCKER_HOST"); dockerHostEnv != "" { config.Host = dockerHostEnv } + if config.Host == "" { + config.Host = "unix:///var/run/docker.sock" + } ryukDisabledEnv := os.Getenv("TESTCONTAINERS_RYUK_DISABLED") if parseBool(ryukDisabledEnv) { From adcd4224c0898273766383eb4fc59610e10ce08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 11:37:15 +0100 Subject: [PATCH 20/37] fix: create the Docker provider properly --- logconsumer_test.go | 10 +++++++++- modules/compose/compose_api.go | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/logconsumer_test.go b/logconsumer_test.go index fe8634f3d4..367c6d5b89 100644 --- a/logconsumer_test.go +++ b/logconsumer_test.go @@ -240,7 +240,15 @@ func TestContainerLogWithErrClosed(t *testing.T) { } defer client.Close() - provider := &DockerProvider{client: client, DockerProviderOptions: &DockerProviderOptions{GenericProviderOptions: &GenericProviderOptions{Logger: TestLogger(t)}}} + provider := &DockerProvider{ + client: client, + config: ReadConfig(), + DockerProviderOptions: &DockerProviderOptions{ + GenericProviderOptions: &GenericProviderOptions{ + Logger: TestLogger(t), + }, + }, + } nginx, err := provider.CreateContainer(ctx, ContainerRequest{Image: "nginx", ExposedPorts: []string{"80/tcp"}}) if err != nil { diff --git a/modules/compose/compose_api.go b/modules/compose/compose_api.go index 5fb3c8a453..c7d35c2692 100644 --- a/modules/compose/compose_api.go +++ b/modules/compose/compose_api.go @@ -309,7 +309,11 @@ func (d *dockerCompose) lookupContainer(ctx context.Context, svcName string) (*t } container.SetLogger(d.logger) - dockerProvider := &testcontainers.DockerProvider{} + dockerProvider, err := testcontainers.NewDockerProvider(testcontainers.WithLogger(d.logger)) + if err != nil { + return nil, err + } + dockerProvider.SetClient(d.dockerClient) container.SetProvider(dockerProvider) From b1d41b08a3bd943473d9ce4cca292a94dac75cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 12:24:05 +0100 Subject: [PATCH 21/37] fix: rename GH check for reaper-off --- .github/workflows/ci-reaper-off.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-reaper-off.yml b/.github/workflows/ci-reaper-off.yml index e9d0f7f58d..29ed1878eb 100644 --- a/.github/workflows/ci-reaper-off.yml +++ b/.github/workflows/ci-reaper-off.yml @@ -18,7 +18,7 @@ jobs: - name: Run gofmt run: | ./scripts/checks.sh - test: + test-reaper-off: strategy: matrix: go-version: [1.19.x, 1.x] From 44e9d42816444b0ea66156859a2d2172c16654a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 12:24:59 +0100 Subject: [PATCH 22/37] chore: remove old static-analysis checks --- .github/workflows/ci-reaper-off.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/ci-reaper-off.yml b/.github/workflows/ci-reaper-off.yml index 29ed1878eb..ef83996c56 100644 --- a/.github/workflows/ci-reaper-off.yml +++ b/.github/workflows/ci-reaper-off.yml @@ -7,17 +7,6 @@ concurrency: cancel-in-progress: true jobs: - static-analysis: - runs-on: ubuntu-latest - steps: - - - uses: actions/checkout@v3 - - name: Run ShellCheck - run: | - shellcheck scripts/*.sh - - name: Run gofmt - run: | - ./scripts/checks.sh test-reaper-off: strategy: matrix: From 7c22bc204e59d6c3425fdc998f722c29468fe657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 12:32:20 +0100 Subject: [PATCH 23/37] fix: remove containers in tests --- docker_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docker_test.go b/docker_test.go index 5ac3cce53c..6c4e1e9827 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1054,15 +1054,16 @@ func TestContainerCreationWaitsForLogContextTimeout(t *testing.T) { }, WaitingFor: wait.ForLog("test context timeout").WithStartupTimeout(1 * time.Second), } - _, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, }) - if err == nil { t.Error("Expected timeout") } + + terminateContainerOnEnd(t, ctx, c) } func TestContainerCreationWaitsForLog(t *testing.T) { @@ -1189,15 +1190,16 @@ func TestContainerCreationWaitsForLogAndPortContextTimeout(t *testing.T) { wait.ForListeningPort("3306/tcp"), ), } - _, err := GenericContainer(ctx, GenericContainerRequest{ + c, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: req, Started: true, }) - if err == nil { t.Fatal("Expected timeout") } + + terminateContainerOnEnd(t, ctx, c) } func TestContainerCreationWaitingForHostPort(t *testing.T) { From d05372786710111eb2c12922f5089294a053862b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 12:48:42 +0100 Subject: [PATCH 24/37] fix: remove more containers in tests --- docker_test.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docker_test.go b/docker_test.go index 6c4e1e9827..5f61400217 100644 --- a/docker_test.go +++ b/docker_test.go @@ -34,12 +34,13 @@ import ( ) const ( - mysqlImage = "docker.io/mysql:8.0.30" - nginxImage = "docker.io/nginx" - nginxAlpineImage = "docker.io/nginx:alpine" - nginxDefaultPort = "80/tcp" - nginxHighPort = "8080/tcp" - daemonMaxVersion = "1.41" + mysqlImage = "docker.io/mysql:8.0.30" + nginxDelayedImage = "docker.io/menedev/delayed-nginx:1.15.2" + nginxImage = "docker.io/nginx" + nginxAlpineImage = "docker.io/nginx:alpine" + nginxDefaultPort = "80/tcp" + nginxHighPort = "8080/tcp" + daemonMaxVersion = "1.41" ) var providerType = ProviderDocker @@ -942,7 +943,7 @@ func TestContainerCreationAndWaitForListeningPortLongEnough(t *testing.T) { nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: "docker.io/menedev/delayed-nginx:1.15.2", + Image: nginxDelayedImage, ExposedPorts: []string{ nginxDefaultPort, }, @@ -973,7 +974,7 @@ func TestContainerCreationTimesOut(t *testing.T) { nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: "docker.io/menedev/delayed-nginx:1.15.2", + Image: nginxDelayedImage, ExposedPorts: []string{ nginxDefaultPort, }, @@ -981,12 +982,11 @@ func TestContainerCreationTimesOut(t *testing.T) { }, Started: true, }) + + terminateContainerOnEnd(t, ctx, nginxC) + if err == nil { t.Error("Expected timeout") - err := nginxC.Terminate(ctx) - if err != nil { - t.Fatal(err) - } } } @@ -1028,7 +1028,7 @@ func TestContainerCreationTimesOutWithHttp(t *testing.T) { nginxC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: "docker.io/menedev/delayed-nginx:1.15.2", + Image: nginxDelayedImage, ExposedPorts: []string{ nginxDefaultPort, }, From 0bd383f68c5980348b373b04beadeeae9fd5bad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 12:59:39 +0100 Subject: [PATCH 25/37] chore: extract to constants --- config_test.go | 91 +++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/config_test.go b/config_test.go index 828969f00c..ac1a1978e6 100644 --- a/config_test.go +++ b/config_test.go @@ -9,6 +9,13 @@ import ( "github.com/stretchr/testify/assert" ) +const ( + dockerSock = "unix:///var/run/docker.sock" + tcpDockerHost1234 = "tcp://127.0.0.1:1234" + tcpDockerHost33293 = "tcp://127.0.0.1:33293" + tcpDockerHost4711 = "tcp://127.0.0.1:4711" +) + func TestReadConfig(t *testing.T) { t.Run("Config is read just once", func(t *testing.T) { t.Setenv("HOME", "") @@ -18,7 +25,7 @@ func TestReadConfig(t *testing.T) { expected := TestcontainersConfig{ RyukDisabled: true, - Host: "unix:///var/run/docker.sock", + Host: dockerSock, } assert.Equal(t, expected, config) @@ -36,7 +43,7 @@ func TestReadTCConfig(t *testing.T) { config := readConfig() expected := TestcontainersConfig{} - expected.Host = "unix:///var/run/docker.sock" + expected.Host = dockerSock assert.Equal(t, expected, config) }) @@ -51,7 +58,7 @@ func TestReadTCConfig(t *testing.T) { expected := TestcontainersConfig{} expected.RyukDisabled = true expected.RyukPrivileged = true - expected.Host = "unix:///var/run/docker.sock" + expected.Host = dockerSock assert.Equal(t, expected, config) }) @@ -63,7 +70,7 @@ func TestReadTCConfig(t *testing.T) { config := readConfig() expected := TestcontainersConfig{} - expected.Host = "unix:///var/run/docker.sock" + expected.Host = dockerSock assert.Equal(t, expected, config) }) @@ -71,11 +78,11 @@ func TestReadTCConfig(t *testing.T) { t.Run("HOME does not contain TC props file - DOCKER_HOST env is set", func(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) - t.Setenv("DOCKER_HOST", "tcp://127.0.0.1:33293") + t.Setenv("DOCKER_HOST", tcpDockerHost33293) config := readConfig() expected := TestcontainersConfig{} - expected.Host = "tcp://127.0.0.1:33293" + expected.Host = tcpDockerHost33293 assert.Equal(t, expected, config) }) @@ -90,7 +97,7 @@ func TestReadTCConfig(t *testing.T) { expected := TestcontainersConfig{} expected.RyukDisabled = true expected.RyukPrivileged = true - expected.Host = "unix:///var/run/docker.sock" + expected.Host = dockerSock assert.Equal(t, expected, config) }) @@ -104,36 +111,36 @@ func TestReadTCConfig(t *testing.T) { }{ { "Single Docker host with spaces", - "docker.host = tcp://127.0.0.1:33293", + "docker.host = " + tcpDockerHost33293, map[string]string{}, TestcontainersConfig{ - Host: "tcp://127.0.0.1:33293", + Host: tcpDockerHost33293, TLSVerify: 0, CertPath: "", }, }, { "Multiple docker host entries, last one wins", - `docker.host = tcp://127.0.0.1:33293 - docker.host = tcp://127.0.0.1:4711 + `docker.host = ` + tcpDockerHost33293 + ` + docker.host = ` + tcpDockerHost4711 + ` `, map[string]string{}, TestcontainersConfig{ - Host: "tcp://127.0.0.1:4711", + Host: tcpDockerHost4711, TLSVerify: 0, CertPath: "", }, }, { "Multiple docker host entries, last one wins, with TLS", - `docker.host = tcp://127.0.0.1:33293 - docker.host = tcp://127.0.0.1:4711 - docker.host = tcp://127.0.0.1:1234 + `docker.host = ` + tcpDockerHost33293 + ` + docker.host = ` + tcpDockerHost4711 + ` + docker.host = ` + tcpDockerHost1234 + ` docker.tls.verify = 1 `, map[string]string{}, TestcontainersConfig{ - Host: "tcp://127.0.0.1:1234", + Host: tcpDockerHost1234, TLSVerify: 1, CertPath: "", }, @@ -143,7 +150,7 @@ func TestReadTCConfig(t *testing.T) { "", map[string]string{}, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", }, @@ -151,44 +158,44 @@ func TestReadTCConfig(t *testing.T) { { "Non-valid properties are ignored", `foo = bar - docker.host = tcp://127.0.0.1:1234 + docker.host = ` + tcpDockerHost1234 + ` `, map[string]string{}, TestcontainersConfig{ - Host: "tcp://127.0.0.1:1234", + Host: tcpDockerHost1234, TLSVerify: 0, CertPath: "", }, }, { "Single Docker host without spaces", - "docker.host=tcp://127.0.0.1:33293", + "docker.host=" + tcpDockerHost33293, map[string]string{}, TestcontainersConfig{ - Host: "tcp://127.0.0.1:33293", + Host: tcpDockerHost33293, TLSVerify: 0, CertPath: "", }, }, { "Comments are ignored", - `#docker.host=tcp://127.0.0.1:33293`, + `#docker.host=` + tcpDockerHost33293, map[string]string{}, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", }, }, { "Multiple docker host entries, last one wins, with TLS and cert path", - `#docker.host = tcp://127.0.0.1:33293 - docker.host = tcp://127.0.0.1:4711 - docker.host = tcp://127.0.0.1:1234 + `#docker.host = ` + tcpDockerHost33293 + ` + docker.host = ` + tcpDockerHost4711 + ` + docker.host = ` + tcpDockerHost1234 + ` docker.cert.path=/tmp/certs`, map[string]string{}, TestcontainersConfig{ - Host: "tcp://127.0.0.1:1234", + Host: tcpDockerHost1234, TLSVerify: 0, CertPath: "/tmp/certs", }, @@ -198,7 +205,7 @@ func TestReadTCConfig(t *testing.T) { `ryuk.disabled=true`, map[string]string{}, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -209,7 +216,7 @@ func TestReadTCConfig(t *testing.T) { `ryuk.container.privileged=true`, map[string]string{}, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -222,7 +229,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "true", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -235,7 +242,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -248,7 +255,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "true", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -261,7 +268,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "true", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -274,7 +281,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "false", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: false, @@ -287,7 +294,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "false", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: false, @@ -300,7 +307,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -313,7 +320,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukPrivileged: true, @@ -326,7 +333,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukPrivileged: false, @@ -339,7 +346,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "false", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukPrivileged: false, @@ -354,7 +361,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "true", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: true, @@ -368,7 +375,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_DISABLED": "foo", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukDisabled: false, @@ -381,7 +388,7 @@ func TestReadTCConfig(t *testing.T) { "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED": "foo", }, TestcontainersConfig{ - Host: "unix:///var/run/docker.sock", + Host: dockerSock, TLSVerify: 0, CertPath: "", RyukPrivileged: false, From e9048f3136ca3f27b1b5b483328fb66407e58438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 13:11:00 +0100 Subject: [PATCH 26/37] chore: enable Ryuk using env vars at GH workflow --- .github/workflows/ci-reaper-off.yml | 9 +++------ .github/workflows/ci.yml | 8 ++------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-reaper-off.yml b/.github/workflows/ci-reaper-off.yml index ef83996c56..5e1b2428ef 100644 --- a/.github/workflows/ci-reaper-off.yml +++ b/.github/workflows/ci-reaper-off.yml @@ -12,8 +12,10 @@ jobs: matrix: go-version: [1.19.x, 1.x] runs-on: ubuntu-latest - steps: + env: + TESTCONTAINERS_RYUK_DISABLED: "true" + steps: - name: Set up Go uses: actions/setup-go@v3 with: @@ -23,11 +25,6 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v3 - - - name: Disable Ryuk at properties side - run: | - set -x - echo 'ryuk.disabled=true' >> ${HOME}/.testcontainers.properties - name: gotestsum # only run tests on linux, there are a number of things that won't allow the tests to run on anything else # many (maybe, all?) images used can only be build on Linux, they don't have Windows in their manifest, and diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e745acb7e..0f3810766f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,8 @@ jobs: go-version: [1.19.x, 1.x] platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} + env: + TESTCONTAINERS_RYUK_DISABLED: "false" steps: - name: Set up Go @@ -35,12 +37,6 @@ jobs: GOOS: linux run: go build - - name: Enable Ryuk at properties side - if: ${{ matrix.platform == 'ubuntu-latest' }} - run: | - set -x - echo 'ryuk.disabled=false' >> ${HOME}/.testcontainers.properties - - name: gotestsum # only run tests on linux, there are a number of things that won't allow the tests to run on anything else # many (maybe, all?) images used can only be build on Linux, they don't have Windows in their manifest, and From f52657b570838128482883deb60853457a12b446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 13:35:23 +0100 Subject: [PATCH 27/37] fix: reset test environment --- config_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/config_test.go b/config_test.go index ac1a1978e6..1ef477dc77 100644 --- a/config_test.go +++ b/config_test.go @@ -16,7 +16,16 @@ const ( tcpDockerHost4711 = "tcp://127.0.0.1:4711" ) +// unset environment variables to avoid side effects +// exectute this function before each test +func resetTestEnv(t *testing.T) { + t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "") + t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "") +} + func TestReadConfig(t *testing.T) { + resetTestEnv(t) + t.Run("Config is read just once", func(t *testing.T) { t.Setenv("HOME", "") t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") @@ -37,6 +46,8 @@ func TestReadConfig(t *testing.T) { } func TestReadTCConfig(t *testing.T) { + resetTestEnv(t) + t.Run("HOME is not set", func(t *testing.T) { t.Setenv("HOME", "") @@ -410,7 +421,6 @@ func TestReadTCConfig(t *testing.T) { config := readConfig() assert.Equal(t, tt.expected, config, "Configuration doesn't not match") - }) } }) From d1d8caa6552aec0f1350f82db704f1c2e2319c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 18:20:10 +0100 Subject: [PATCH 28/37] fix: typo --- config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_test.go b/config_test.go index 1ef477dc77..300dd66bca 100644 --- a/config_test.go +++ b/config_test.go @@ -17,7 +17,7 @@ const ( ) // unset environment variables to avoid side effects -// exectute this function before each test +// execute this function before each test func resetTestEnv(t *testing.T) { t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "") t.Setenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED", "") From e652d2a68082fa64f82de914e22294b2722e038c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 18:37:28 +0100 Subject: [PATCH 29/37] chore: log when the properties file is found --- config.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.go b/config.go index 7567ccaa32..aad044e80e 100644 --- a/config.go +++ b/config.go @@ -84,6 +84,8 @@ func readConfig() TestcontainersConfig { return applyEnvironmentConfiguration(config) } + fmt.Printf("Testcontainers properties file has been found: %s\n", tcProp) + return applyEnvironmentConfiguration(config) } From acf23a16ba46bc347ed217f6105a05af5a62a7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 18:47:22 +0100 Subject: [PATCH 30/37] fix: reset config's syncOnce in tests --- config.go | 3 ++- docker_test.go | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config.go b/config.go index aad044e80e..36e2edd754 100644 --- a/config.go +++ b/config.go @@ -11,7 +11,7 @@ import ( ) var tcConfig TestcontainersConfig -var tcConfigOnce sync.Once +var tcConfigOnce *sync.Once = new(sync.Once) // TestcontainersConfig represents the configuration for Testcontainers type TestcontainersConfig struct { @@ -35,6 +35,7 @@ Ryuk has been disabled for the current execution. This can cause unexpected beha More on this: https://golang.testcontainers.org/features/garbage_collector/ **********************************************************************************************` Logger.Printf(ryukDisabledMessage) + Logger.Printf("\n%+v", tcConfig) } }) diff --git a/docker_test.go b/docker_test.go index 5f61400217..2318951a55 100644 --- a/docker_test.go +++ b/docker_test.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log" + "sync" "io" "math/rand" @@ -360,6 +361,7 @@ func TestContainerReturnItsContainerID(t *testing.T) { } func TestContainerStartsWithoutTheReaper(t *testing.T) { + tcConfigOnce = new(sync.Once) // reset the config tcConfig := ReadConfig() if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") @@ -399,6 +401,7 @@ func TestContainerStartsWithoutTheReaper(t *testing.T) { } func TestContainerStartsWithTheReaper(t *testing.T) { + tcConfigOnce = new(sync.Once) // reset the config tcConfig := ReadConfig() if tcConfig.RyukDisabled { t.Skip("Ryuk is disabled, skipping test") @@ -441,6 +444,7 @@ func TestContainerStartsWithTheReaper(t *testing.T) { } func TestContainerTerminationResetsState(t *testing.T) { + tcConfigOnce = new(sync.Once) // reset the config tcConfig := ReadConfig() if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") @@ -610,6 +614,7 @@ func TestContainerTerminationWithReaper(t *testing.T) { } func TestContainerTerminationWithoutReaper(t *testing.T) { + tcConfigOnce = new(sync.Once) // reset the config tcConfig := ReadConfig() if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") From dda07cf2cc945e8d9b84b6a073e1a23e27118791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 15 Mar 2023 19:14:52 +0100 Subject: [PATCH 31/37] chore: proper config state in tests --- docker_test.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docker_test.go b/docker_test.go index 2318951a55..7199d8a1b2 100644 --- a/docker_test.go +++ b/docker_test.go @@ -444,12 +444,6 @@ func TestContainerStartsWithTheReaper(t *testing.T) { } func TestContainerTerminationResetsState(t *testing.T) { - tcConfigOnce = new(sync.Once) // reset the config - tcConfig := ReadConfig() - if !tcConfig.RyukDisabled { - t.Skip("Ryuk is enabled, skipping test") - } - ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ @@ -538,6 +532,12 @@ func TestContainerStateAfterTermination(t *testing.T) { } func TestContainerStopWithReaper(t *testing.T) { + tcConfigOnce = new(sync.Once) // reset the config + tcConfig := ReadConfig() + if tcConfig.RyukDisabled { + t.Skip("Ryuk is disabled, skipping test") + } + ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ @@ -580,6 +580,12 @@ func TestContainerStopWithReaper(t *testing.T) { } func TestContainerTerminationWithReaper(t *testing.T) { + tcConfigOnce = new(sync.Once) // reset the config + tcConfig := ReadConfig() + if tcConfig.RyukDisabled { + t.Skip("Ryuk is disabled, skipping test") + } + ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ From 1f2e835bf80bc3462457e61d97dbff831e333f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 16 Mar 2023 07:42:11 +0100 Subject: [PATCH 32/37] chore: do not reset the sync --- docker_test.go | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/docker_test.go b/docker_test.go index 7199d8a1b2..eec5f7cb95 100644 --- a/docker_test.go +++ b/docker_test.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "sync" "io" "math/rand" @@ -361,8 +360,7 @@ func TestContainerReturnItsContainerID(t *testing.T) { } func TestContainerStartsWithoutTheReaper(t *testing.T) { - tcConfigOnce = new(sync.Once) // reset the config - tcConfig := ReadConfig() + tcConfig := readConfig() // read the config using the private method to avoid the sync.Once if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") } @@ -401,8 +399,7 @@ func TestContainerStartsWithoutTheReaper(t *testing.T) { } func TestContainerStartsWithTheReaper(t *testing.T) { - tcConfigOnce = new(sync.Once) // reset the config - tcConfig := ReadConfig() + tcConfig := readConfig() // read the config using the private method to avoid the sync.Once if tcConfig.RyukDisabled { t.Skip("Ryuk is disabled, skipping test") } @@ -532,8 +529,7 @@ func TestContainerStateAfterTermination(t *testing.T) { } func TestContainerStopWithReaper(t *testing.T) { - tcConfigOnce = new(sync.Once) // reset the config - tcConfig := ReadConfig() + tcConfig := readConfig() // read the config using the private method to avoid the sync.Once if tcConfig.RyukDisabled { t.Skip("Ryuk is disabled, skipping test") } @@ -580,8 +576,7 @@ func TestContainerStopWithReaper(t *testing.T) { } func TestContainerTerminationWithReaper(t *testing.T) { - tcConfigOnce = new(sync.Once) // reset the config - tcConfig := ReadConfig() + tcConfig := readConfig() // read the config using the private method to avoid the sync.Once if tcConfig.RyukDisabled { t.Skip("Ryuk is disabled, skipping test") } @@ -620,8 +615,7 @@ func TestContainerTerminationWithReaper(t *testing.T) { } func TestContainerTerminationWithoutReaper(t *testing.T) { - tcConfigOnce = new(sync.Once) // reset the config - tcConfig := ReadConfig() + tcConfig := readConfig() // read the config using the private method to avoid the sync.Once if !tcConfig.RyukDisabled { t.Skip("Ryuk is enabled, skipping test") } From dd628e7b1ded419a3b3fdab89d2e8de7414e7011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 16 Mar 2023 08:01:07 +0100 Subject: [PATCH 33/37] fix: remove containers in tests --- docker_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker_test.go b/docker_test.go index eec5f7cb95..c1d94d1aee 100644 --- a/docker_test.go +++ b/docker_test.go @@ -411,7 +411,7 @@ func TestContainerStartsWithTheReaper(t *testing.T) { } defer client.Close() - _, err = GenericContainer(ctx, GenericContainerRequest{ + c, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ Image: nginxAlpineImage, @@ -424,6 +424,8 @@ func TestContainerStartsWithTheReaper(t *testing.T) { if err != nil { t.Fatal(err) } + terminateContainerOnEnd(t, ctx, c) + filtersJSON := fmt.Sprintf(`{"label":{"%s":true}}`, testcontainersdocker.LabelReaper) f, err := filters.FromJSON(filtersJSON) if err != nil { From fdca162285d00f17a38a22e1b50163500bd9badd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 16 Mar 2023 08:08:16 +0100 Subject: [PATCH 34/37] chore: remove duplicated test --- docker_test.go | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/docker_test.go b/docker_test.go index c1d94d1aee..7f5bdf7f7e 100644 --- a/docker_test.go +++ b/docker_test.go @@ -301,44 +301,6 @@ func TestContainerWithHostNetworkAndEndpoint(t *testing.T) { } } -func TestContainerWithHostNetworkAndPortEndpoint(t *testing.T) { - ctx := context.Background() - - absPath, err := filepath.Abs("./testresources/nginx-highport.conf") - if err != nil { - t.Fatal(err) - } - - gcr := GenericContainerRequest{ - ProviderType: providerType, - ContainerRequest: ContainerRequest{ - Image: nginxAlpineImage, - WaitingFor: wait.ForListeningPort(nginxHighPort), - Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), - HostConfigModifier: func(hc *container.HostConfig) { - hc.NetworkMode = "host" - }, - }, - Started: true, - } - - nginxC, err := GenericContainer(ctx, gcr) - - require.NoError(t, err) - terminateContainerOnEnd(t, ctx, nginxC) - - origin, err := nginxC.PortEndpoint(ctx, nginxHighPort, "http") - if err != nil { - t.Errorf("Expected host %s. Got '%d'.", origin, err) - } - t.Log(origin) - - _, err = http.Get(origin) - if err != nil { - t.Errorf("Expected OK response. Got '%d'.", err) - } -} - func TestContainerReturnItsContainerID(t *testing.T) { ctx := context.Background() nginxA, err := GenericContainer(ctx, GenericContainerRequest{ From b981a9e5646ace4582f62ac57761e084bd82c482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 20 Mar 2023 17:15:29 +0100 Subject: [PATCH 35/37] docs: document the config --- config.go | 3 ++ docs/features/configuration.md | 63 ++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 67 insertions(+) create mode 100644 docs/features/configuration.md diff --git a/config.go b/config.go index 36e2edd754..664f8c5ad5 100644 --- a/config.go +++ b/config.go @@ -14,6 +14,7 @@ var tcConfig TestcontainersConfig var tcConfigOnce *sync.Once = new(sync.Once) // TestcontainersConfig represents the configuration for Testcontainers +// testcontainersConfig { type TestcontainersConfig struct { Host string `properties:"docker.host,default="` TLSVerify int `properties:"docker.tls.verify,default=0"` @@ -22,6 +23,8 @@ type TestcontainersConfig struct { RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` } +// } + // ReadConfig reads from testcontainers properties file, storing the result in a singleton instance // of the TestcontainersConfig struct func ReadConfig() TestcontainersConfig { diff --git a/docs/features/configuration.md b/docs/features/configuration.md new file mode 100644 index 0000000000..5aa4c3aa18 --- /dev/null +++ b/docs/features/configuration.md @@ -0,0 +1,63 @@ +# Custom configuration + +You can override some default properties if your environment requires that. + +## Configuration locations +The configuration will be loaded from multiple locations. Properties are considered in the following order: + +1. Environment variables +2. `.testcontainers.properties` in user's home folder. Example locations: +**Linux:** `/home/myuser/.testcontainers.properties` +**Windows:** `C:/Users/myuser/.testcontainers.properties` +**macOS:** `/Users/myuser/.testcontainers.properties` + +Note that when using environment variables, configuration property names should be set in upper +case with underscore separators, preceded by `TESTCONTAINERS_` - e.g. `ryuk.disabled` becomes +`TESTCONTAINERS_RYUK_DISABLED`. + +If any keys conflict, the value will be taken on the basis of the first value found in: + +### Supported properties + +_Testcontainers for Go_ provides a struct type to represent the configuration: + + +[Supported properties](../../config.go) inside_block:testcontainersConfig + + +You can read it with the `ReadConfig()` function: + +```go +cfg := testcontainers.ReadConfig() +``` + +### Disabling Ryuk +Ryuk must be started as a privileged container. +If your environment already implements automatic cleanup of containers after the execution, +but does not allow starting privileged containers, you can turn off the Ryuk container by setting +`TESTCONTAINERS_RYUK_DISABLED` **environment variable** to `true`. + +!!!info + For more information about Ryuk, see [Garbage Collector](garbage_collector.md). + +## Customizing Docker host detection + +Testcontainers will attempt to detect the Docker environment and configure everything to work automatically. + +However, sometimes customization is required. Testcontainers will respect the following **environment variables**: + +> **DOCKER_HOST** = unix:///var/run/docker.sock +> See [Docker environment variables](https://docs.docker.com/engine/reference/commandline/cli/#environment-variables) +> +> **TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE** +> Path to Docker's socket. Used by Ryuk, Docker Compose, and a few other containers that need to perform Docker actions. +> Example: `/var/run/docker-alt.sock` + +For advanced users, the Docker host connection can be configured **via configuration** in `~/.testcontainers.properties`. +The example below illustrates usage: + +```properties +docker.host=tcp\://my.docker.host\:1234 # Equivalent to the DOCKER_HOST environment variable. Colons should be escaped. +docker.tls.verify=1 # Equivalent to the DOCKER_TLS_VERIFY environment variable +docker.cert.path=/some/path # Equivalent to the DOCKER_CERT_PATH environment variable +``` diff --git a/mkdocs.yml b/mkdocs.yml index 93b20983cc..94789ac53e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,6 +30,7 @@ nav: - Quickstart: quickstart.md - Features: - features/creating_container.md + - features/configuration.md - features/networking.md - features/garbage_collector.md - features/build_from_dockerfile.md From e22dafcf6d7b842e32e53110f705eb87fe3b9a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 20 Mar 2023 17:17:30 +0100 Subject: [PATCH 36/37] fix: remove paragraph --- docs/features/configuration.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 5aa4c3aa18..cc40f7fa94 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -15,8 +15,6 @@ Note that when using environment variables, configuration property names should case with underscore separators, preceded by `TESTCONTAINERS_` - e.g. `ryuk.disabled` becomes `TESTCONTAINERS_RYUK_DISABLED`. -If any keys conflict, the value will be taken on the basis of the first value found in: - ### Supported properties _Testcontainers for Go_ provides a struct type to represent the configuration: From ee9a64010c1b57ccbe73240a19a70b1f04a1ac8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 20 Mar 2023 17:18:52 +0100 Subject: [PATCH 37/37] fix: proper escaping message --- docs/features/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/configuration.md b/docs/features/configuration.md index cc40f7fa94..c5c650c331 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -55,7 +55,7 @@ For advanced users, the Docker host connection can be configured **via configura The example below illustrates usage: ```properties -docker.host=tcp\://my.docker.host\:1234 # Equivalent to the DOCKER_HOST environment variable. Colons should be escaped. +docker.host=tcp://my.docker.host:1234 # Equivalent to the DOCKER_HOST environment variable. docker.tls.verify=1 # Equivalent to the DOCKER_TLS_VERIFY environment variable docker.cert.path=/some/path # Equivalent to the DOCKER_CERT_PATH environment variable ```