Skip to content

Commit

Permalink
test: Add functions to check and rewrite local URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
mattevans committed Jan 9, 2025
1 parent 72dd333 commit 4ba0b3e
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 30 deletions.
3 changes: 1 addition & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ services:
ports:
- "${CONTRIBUTOOR_METRICS_ADDRESS}:${CONTRIBUTOOR_METRICS_PORT}:${CONTRIBUTOOR_METRICS_PORT}"
extra_hosts:
- "127.0.0.1:host-gateway"
- "localhost:host-gateway"
- "host.docker.internal:host-gateway"
volumes:
- ${CONTRIBUTOOR_CONFIG_PATH}/config.yaml:/config/config.yaml:ro
restart: always
Expand Down
96 changes: 68 additions & 28 deletions internal/sidecar/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import (

//go:generate mockgen -package mock -destination mock/docker.mock.go github.com/ethpandaops/contributoor-installer/internal/sidecar DockerSidecar

var localHostnames = map[string]bool{
"localhost": true,
"127.0.0.1": true,
"0.0.0.0": true,
}

type DockerSidecar interface {
SidecarRunner
}
Expand Down Expand Up @@ -124,35 +130,32 @@ func (s *dockerSidecar) Update() error {
return nil
}

func (s *dockerSidecar) getComposeEnv() []string {
cfg := s.sidecarCfg.Get()
// IsLocalURL checks if the given URL is pointing to a local address.
func IsLocalURL(url string) bool {
for hostname := range localHostnames {
if strings.Contains(url, hostname) {
return true
}
}

env := append(
os.Environ(),
fmt.Sprintf("CONTRIBUTOOR_CONFIG_PATH=%s", filepath.Dir(s.configPath)),
fmt.Sprintf("CONTRIBUTOOR_VERSION=%s", cfg.Version),
)
return false
}

// Handle metrics address (always added).
metricsHost, metricsPort := cfg.GetMetricsHostPort()
env = append(
env,
fmt.Sprintf("CONTRIBUTOOR_METRICS_ADDRESS=%s", metricsHost),
fmt.Sprintf("CONTRIBUTOOR_METRICS_PORT=%s", metricsPort),
)
// RewriteBeaconURL rewrites local URLs to use host.docker.internal.
func RewriteBeaconURL(url string) string {
if !IsLocalURL(url) {
return url
}

// Handle pprof address (only added if set).
if pprofHost, pprofPort := cfg.GetPprofHostPort(); pprofHost != "" {
env = append(
env,
fmt.Sprintf("CONTRIBUTOOR_PPROF_ADDRESS=%s", pprofHost),
fmt.Sprintf("CONTRIBUTOOR_PPROF_PORT=%s", pprofPort),
)
// Replace all local hostnames with host.docker.internal.
for hostname := range localHostnames {
url = strings.Replace(url, hostname, "host.docker.internal", 1)
}

return env
return url
}

// findComposeFile finds the docker-compose file based on the OS.
func findComposeFile() (string, error) {
// Get binary directory
ex, err := os.Executable()
Expand All @@ -162,18 +165,17 @@ func findComposeFile() (string, error) {

binDir := filepath.Dir(ex)

cwd, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("could not get working directory: %w", err)
}

// Check release mode (next to binary)
composePath := filepath.Join(binDir, "docker-compose.yml")
if _, e := os.Stat(composePath); e == nil {
return composePath, nil
}

// Check dev mode paths
cwd, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("could not get working directory: %w", err)
}

// Try current directory
if _, err := os.Stat(filepath.Join(cwd, "docker-compose.yml")); err == nil {
return filepath.Join(cwd, "docker-compose.yml"), nil
Expand Down Expand Up @@ -212,3 +214,41 @@ func validateComposePath(path string) error {

return nil
}

func (s *dockerSidecar) getComposeEnv() []string {
cfg := s.sidecarCfg.Get()

// Docker containers can't directly access the host via localhost/127.0.0.1.
// We rewrite these to host.docker.internal which resolves differently per platform:
// - macOS: Built-in DNS name that points to the Docker Desktop VM's gateway
// - Linux: Maps to host-gateway via extra_hosts in docker-compose.yml
// This provides a consistent way to access the host machine across platforms.
if cfg.BeaconNodeAddress != "" {
cfg.BeaconNodeAddress = RewriteBeaconURL(cfg.BeaconNodeAddress)
}

env := append(
os.Environ(),
fmt.Sprintf("CONTRIBUTOOR_CONFIG_PATH=%s", filepath.Dir(s.configPath)),
fmt.Sprintf("CONTRIBUTOOR_VERSION=%s", cfg.Version),
)

// Handle metrics address (always added).
metricsHost, metricsPort := cfg.GetMetricsHostPort()
env = append(
env,
fmt.Sprintf("CONTRIBUTOOR_METRICS_ADDRESS=%s", metricsHost),
fmt.Sprintf("CONTRIBUTOOR_METRICS_PORT=%s", metricsPort),
)

// Handle pprof address (only added if set).
if pprofHost, pprofPort := cfg.GetPprofHostPort(); pprofHost != "" {
env = append(
env,
fmt.Sprintf("CONTRIBUTOOR_PPROF_ADDRESS=%s", pprofHost),
fmt.Sprintf("CONTRIBUTOOR_PPROF_PORT=%s", pprofPort),
)
}

return env
}
92 changes: 92 additions & 0 deletions internal/sidecar/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,95 @@ func TestDockerService_Integration(t *testing.T) {
require.False(t, running)
})
}

func TestIsLocalURL(t *testing.T) {
tests := []struct {
name string
url string
want bool
}{
{
name: "localhost",
url: "http://localhost:5052",
want: true,
},
{
name: "127.0.0.1",
url: "http://127.0.0.1:5052",
want: true,
},
{
name: "0.0.0.0",
url: "http://0.0.0.0:5052",
want: true,
},
{
name: "remote url",
url: "http://example.com:5052",
want: false,
},
{
name: "empty url",
url: "",
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := sidecar.IsLocalURL(tt.url)
require.Equal(t, tt.want, got)
})
}
}

func TestRewriteBeaconURL(t *testing.T) {
tests := []struct {
name string
url string
want string
}{
{
name: "localhost",
url: "http://localhost:5052",
want: "http://host.docker.internal:5052",
},
{
name: "127.0.0.1",
url: "http://127.0.0.1:5052",
want: "http://host.docker.internal:5052",
},
{
name: "0.0.0.0",
url: "http://0.0.0.0:5052",
want: "http://host.docker.internal:5052",
},
{
name: "remote url",
url: "http://example.com:5052",
want: "http://example.com:5052",
},
{
name: "localhost with path",
url: "http://localhost:5052/eth/v1/node/syncing",
want: "http://host.docker.internal:5052/eth/v1/node/syncing",
},
{
name: "localhost with protocol and path",
url: "https://localhost:5052/eth/v1/node/syncing",
want: "https://host.docker.internal:5052/eth/v1/node/syncing",
},
{
name: "empty url",
url: "",
want: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := sidecar.RewriteBeaconURL(tt.url)
require.Equal(t, tt.want, got)
})
}
}

0 comments on commit 4ba0b3e

Please sign in to comment.