From d7e0d34ff5ae7bc6a504be391c24300384842da0 Mon Sep 17 00:00:00 2001 From: Romain Laurent Date: Wed, 2 Oct 2024 10:28:26 +0200 Subject: [PATCH] feat(ryuk): make listen address of exposed port configurable --- internal/config/config.go | 22 +++++++++++++++++++++- internal/config/config_test.go | 23 +++++++++++++++++++++++ reaper.go | 9 +++++++-- reaper_test.go | 16 ++++++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index a172fa3a165..0c571dc0d05 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,6 +2,7 @@ package config import ( "fmt" + "net" "os" "path/filepath" "strconv" @@ -11,7 +12,10 @@ import ( "github.com/magiconair/properties" ) -const ReaperDefaultImage = "testcontainers/ryuk:0.9.0" +const ( + ReaperDefaultImage = "testcontainers/ryuk:0.9.0" + ReaperDefaultPort = "8080/tcp" +) var ( tcConfig Config @@ -66,6 +70,12 @@ type Config struct { // Environment variable: TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` + // RyukListenAddress is the address where the Garbage Collector container is listening. Should be a valid IP address. + // Default make the container listen on 0.0.0.0 . + // + // Environment variable: TESTCONTAINERS_RYUK_ADDRESS + RyukListenAddress string `properties:"ryuk.container.address,default="` + // RyukReconnectionTimeout is the time to wait before attempting to reconnect to the Garbage Collector container. // // Environment variable: TESTCONTAINERS_RYUK_RECONNECTION_TIMEOUT @@ -126,6 +136,11 @@ func read() Config { config.RyukPrivileged = ryukPrivilegedEnv == "true" } + ryukListenAddress := os.Getenv("TESTCONTAINERS_RYUK_ADDRESS") + if parseIP(ryukListenAddress) { + config.RyukListenAddress = ryukListenAddress + } + ryukVerboseEnv := os.Getenv("TESTCONTAINERS_RYUK_VERBOSE") if parseBool(ryukVerboseEnv) { config.RyukVerbose = ryukVerboseEnv == "true" @@ -168,3 +183,8 @@ func parseBool(input string) bool { _, err := strconv.ParseBool(input) return err == nil } + +func parseIP(input string) bool { + ip := net.ParseIP(input) + return ip != nil +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 319deb85b7b..2bfc28adb78 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -24,6 +24,7 @@ func resetTestEnv(t *testing.T) { t.Setenv("TESTCONTAINERS_RYUK_VERBOSE", "") t.Setenv("TESTCONTAINERS_RYUK_RECONNECTION_TIMEOUT", "") t.Setenv("TESTCONTAINERS_RYUK_CONNECTION_TIMEOUT", "") + t.Setenv("TESTCONTAINERS_RYUK_ADDRESS", "") } func TestReadConfig(t *testing.T) { @@ -480,6 +481,28 @@ func TestReadTCConfig(t *testing.T) { }, defaultConfig, }, + { + "With Ryuk listen address configured using properties", + `ryuk.container.address=127.0.0.1`, + map[string]string{}, + Config{ + RyukConnectionTimeout: defaultRyukConnectionTimeout, + RyukReconnectionTimeout: defaultRyukReconnectionTimeout, + RyukListenAddress: "127.0.0.1", + }, + }, + { + "With Ryuk listen address using an env var and properties. Env var wins", + `ryuk.container.address=127.0.0.1`, + map[string]string{ + "TESTCONTAINERS_RYUK_ADDRESS": "172.17.0.1", + }, + Config{ + RyukConnectionTimeout: defaultRyukConnectionTimeout, + RyukReconnectionTimeout: defaultRyukReconnectionTimeout, + RyukListenAddress: "172.17.0.1", + }, + }, { "With Hub image name prefix set as a property", `hub.image.name.prefix=` + defaultHubPrefix + `/props/`, diff --git a/reaper.go b/reaper.go index c41520b5b73..ec094890abd 100644 --- a/reaper.go +++ b/reaper.go @@ -240,13 +240,18 @@ func newReaper(ctx context.Context, sessionID string, provider ReaperProvider) ( SessionID: sessionID, } - listeningPort := nat.Port("8080/tcp") + listeningPort := nat.Port(config.ReaperDefaultPort) tcConfig := provider.Config().Config + exposedPort := string(listeningPort) + if tcConfig.RyukListenAddress != "" { + exposedPort = tcConfig.RyukListenAddress + "::" + exposedPort + } + req := ContainerRequest{ Image: config.ReaperDefaultImage, - ExposedPorts: []string{string(listeningPort)}, + ExposedPorts: []string{exposedPort}, Labels: core.DefaultLabels(sessionID), Privileged: tcConfig.RyukPrivileged, WaitingFor: wait.ForListeningPort(listeningPort), diff --git a/reaper_test.go b/reaper_test.go index f421c2686d4..efa196bf10a 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -408,6 +408,22 @@ func Test_NewReaper(t *testing.T) { "TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX": "registry.mycompany.com/mirror", }, }, + { + name: "Reaper with a custom listen address", + req: createContainerRequest(func(req ContainerRequest) ContainerRequest { + req.ExposedPorts = []string{ + "127.0.0.1::8080/tcp", + } + req.Image = config.ReaperDefaultImage + return req + }), + config: TestcontainersConfig{Config: config.Config{ + RyukListenAddress: "127.0.0.1", + RyukConnectionTimeout: time.Minute, + RyukReconnectionTimeout: 10 * time.Second, + }}, + env: map[string]string{}, + }, } for _, test := range tests {