Skip to content

Commit

Permalink
feat(ryuk): make listen address of exposed port configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
Romain Laurent committed Oct 2, 2024
1 parent 7c53667 commit c5bec9c
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
26 changes: 25 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"fmt"
"net"
"os"
"path/filepath"
"strconv"
Expand All @@ -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
Expand Down Expand Up @@ -66,6 +70,12 @@ type Config struct {
// Environment variable: TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED
RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"`

// RyukAddress 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
RyukAddress 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
Expand Down Expand Up @@ -126,6 +136,11 @@ func read() Config {
config.RyukPrivileged = ryukPrivilegedEnv == "true"
}

ryukAddress := os.Getenv("TESTCONTAINERS_RYUK_ADDRESS")
if ryukAddress != "" {
config.RyukAddress = parseIP(ryukAddress)
}

ryukVerboseEnv := os.Getenv("TESTCONTAINERS_RYUK_VERBOSE")
if parseBool(ryukVerboseEnv) {
config.RyukVerbose = ryukVerboseEnv == "true"
Expand Down Expand Up @@ -168,3 +183,12 @@ func parseBool(input string) bool {
_, err := strconv.ParseBool(input)
return err == nil
}

func parseIP(input string) string {
ip := net.ParseIP(input)
if ip == nil {
panic("invalid IP address: " + input)
}

return ip.String()
}
33 changes: 33 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
Expand All @@ -24,6 +25,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) {
Expand Down Expand Up @@ -140,6 +142,15 @@ func TestReadTCConfig(t *testing.T) {
assert.Equal(t, expected, config)
})

t.Run("Invalid IP address", func(t *testing.T) {
t.Setenv("TESTCONTAINERS_RYUK_ADDRESS", "invalid")
t.Cleanup(func() {
os.Unsetenv("TESTCONTAINERS_RYUK_ADDRESS")
})

require.Panics(t, func() { read() })
})

t.Run("HOME contains TC properties file", func(t *testing.T) {
defaultRyukConnectionTimeout := 60 * time.Second
defaultRyukReconnectionTimeout := 10 * time.Second
Expand Down Expand Up @@ -480,6 +491,28 @@ func TestReadTCConfig(t *testing.T) {
},
defaultConfig,
},
{
"address-properties",
`ryuk.container.address=127.0.0.1`,
map[string]string{},
Config{
RyukConnectionTimeout: defaultRyukConnectionTimeout,
RyukReconnectionTimeout: defaultRyukReconnectionTimeout,
RyukAddress: parseIP("127.0.0.1"),
},
},
{
"address-env-override",
`ryuk.container.address=127.0.0.1`,
map[string]string{
"TESTCONTAINERS_RYUK_ADDRESS": "172.17.0.1",
},
Config{
RyukConnectionTimeout: defaultRyukConnectionTimeout,
RyukReconnectionTimeout: defaultRyukReconnectionTimeout,
RyukAddress: parseIP("172.17.0.1"),
},
},
{
"With Hub image name prefix set as a property",
`hub.image.name.prefix=` + defaultHubPrefix + `/props/`,
Expand Down
9 changes: 7 additions & 2 deletions reaper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.RyukAddress != "" {
exposedPort = net.JoinHostPort(tcConfig.RyukAddress, "") + ":" + exposedPort
}

req := ContainerRequest{
Image: config.ReaperDefaultImage,
ExposedPorts: []string{string(listeningPort)},
ExposedPorts: []string{exposedPort},
Labels: core.DefaultLabels(sessionID),
Privileged: tcConfig.RyukPrivileged,
WaitingFor: wait.ForListeningPort(listeningPort),
Expand Down
16 changes: 16 additions & 0 deletions reaper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
RyukAddress: "127.0.0.1",
RyukConnectionTimeout: time.Minute,
RyukReconnectionTimeout: 10 * time.Second,
}},
env: map[string]string{},
},
}

for _, test := range tests {
Expand Down

0 comments on commit c5bec9c

Please sign in to comment.