diff --git a/docker.go b/docker.go index 6c44d8310c..e7a56b8d1a 100644 --- a/docker.go +++ b/docker.go @@ -139,7 +139,8 @@ func (c *DockerContainer) Endpoint(ctx context.Context, proto string) (string, e } // PortEndpoint gets proto://host:port string for the given exposed port -// Will returns just host:port if proto is "" +// It returns proto://host:port or proto://[IPv6host]:port string for the given exposed port. +// It returns just host:port or [IPv6host]:port if proto is blank. func (c *DockerContainer) PortEndpoint(ctx context.Context, port nat.Port, proto string) (string, error) { host, err := c.Host(ctx) if err != nil { @@ -151,12 +152,12 @@ func (c *DockerContainer) PortEndpoint(ctx context.Context, port nat.Port, proto return "", err } - protoFull := "" - if proto != "" { - protoFull = proto + "://" + hostPort := net.JoinHostPort(host, outerPort.Port()) + if proto == "" { + return hostPort, nil } - return fmt.Sprintf("%s%s:%s", protoFull, host, outerPort.Port()), nil + return proto + "://" + hostPort, nil } // Host gets host (ip or name) of the docker daemon where the container port is exposed @@ -1445,7 +1446,7 @@ func (p *DockerProvider) attemptToPullImage(ctx context.Context, tag string, pul defer pull.Close() // download of docker image finishes at EOF of the pull request - _, err = io.ReadAll(pull) + _, err = io.Copy(io.Discard, pull) return err } diff --git a/examples/nginx/nginx.go b/examples/nginx/nginx.go index 6a5718c3e0..78ab6291a2 100644 --- a/examples/nginx/nginx.go +++ b/examples/nginx/nginx.go @@ -2,7 +2,6 @@ package nginx import ( "context" - "fmt" "time" "github.com/testcontainers/testcontainers-go" @@ -32,16 +31,11 @@ func startContainer(ctx context.Context) (*nginxContainer, error) { return nginxC, err } - ip, err := container.Host(ctx) + endpoint, err := container.PortEndpoint(ctx, "80", "http") if err != nil { return nginxC, err } - mappedPort, err := container.MappedPort(ctx, "80") - if err != nil { - return nginxC, err - } - - nginxC.URI = fmt.Sprintf("http://%s:%s", ip, mappedPort.Port()) + nginxC.URI = endpoint return nginxC, nil } diff --git a/lifecycle.go b/lifecycle.go index 4902bb80e4..6a57a3ea07 100644 --- a/lifecycle.go +++ b/lifecycle.go @@ -311,7 +311,11 @@ func (c *DockerContainer) printLogs(ctx context.Context, cause error) { b, err := io.ReadAll(reader) if err != nil { - c.logger.Printf("failed reading container logs: %v\n", err) + if len(b) > 0 { + c.logger.Printf("failed reading container logs: %v\npartial container logs (%s):\n%s", err, cause, b) + } else { + c.logger.Printf("failed reading container logs: %v\n", err) + } return } diff --git a/modules/azure/azurite/azurite.go b/modules/azure/azurite/azurite.go index 5ce648c480..e58fb53b72 100644 --- a/modules/azure/azurite/azurite.go +++ b/modules/azure/azurite/azurite.go @@ -55,11 +55,6 @@ func (c *Container) TableServiceURL(ctx context.Context) (string, error) { } func (c *Container) serviceURL(ctx context.Context, srv service) (string, error) { - hostname, err := c.Host(ctx) - if err != nil { - return "", fmt.Errorf("host: %w", err) - } - var port nat.Port switch srv { case blobService: @@ -72,12 +67,7 @@ func (c *Container) serviceURL(ctx context.Context, srv service) (string, error) return "", fmt.Errorf("unknown service: %s", srv) } - mappedPort, err := c.MappedPort(ctx, port) - if err != nil { - return "", fmt.Errorf("mapped port: %w", err) - } - - return fmt.Sprintf("http://%s:%d", hostname, mappedPort.Int()), nil + return c.PortEndpoint(ctx, port, "http") } // Run creates an instance of the Azurite container type diff --git a/modules/cassandra/cassandra.go b/modules/cassandra/cassandra.go index e63d1c7e97..5e4fc4e58a 100644 --- a/modules/cassandra/cassandra.go +++ b/modules/cassandra/cassandra.go @@ -25,17 +25,7 @@ type CassandraContainer struct { // ConnectionHost returns the host and port of the cassandra container, using the default, native 9000 port, and // obtaining the host and exposed port from the container func (c *CassandraContainer) ConnectionHost(ctx context.Context) (string, error) { - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - port, err := c.MappedPort(ctx, port) - if err != nil { - return "", err - } - - return host + ":" + port.Port(), nil + return c.PortEndpoint(ctx, port, "") } // WithConfigFile sets the YAML config file to be used for the cassandra container diff --git a/modules/chroma/chroma.go b/modules/chroma/chroma.go index e1c3d6e3bc..3a1921ef1b 100644 --- a/modules/chroma/chroma.go +++ b/modules/chroma/chroma.go @@ -2,7 +2,6 @@ package chroma import ( "context" - "errors" "fmt" "github.com/testcontainers/testcontainers-go" @@ -60,15 +59,5 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom // RESTEndpoint returns the REST endpoint of the Chroma container func (c *ChromaContainer) RESTEndpoint(ctx context.Context) (string, error) { - containerPort, err := c.MappedPort(ctx, "8000/tcp") - if err != nil { - return "", fmt.Errorf("failed to get container port: %w", err) - } - - host, err := c.Host(ctx) - if err != nil { - return "", errors.New("failed to get container host") - } - - return fmt.Sprintf("http://%s:%s", host, containerPort.Port()), nil + return c.PortEndpoint(ctx, "8000/tcp", "http") } diff --git a/modules/clickhouse/clickhouse.go b/modules/clickhouse/clickhouse.go index 499c020bfd..6f3cef4d33 100644 --- a/modules/clickhouse/clickhouse.go +++ b/modules/clickhouse/clickhouse.go @@ -42,17 +42,7 @@ type ClickHouseContainer struct { // ConnectionHost returns the host and port of the clickhouse container, using the default, native 9000 port, and // obtaining the host and exposed port from the container func (c *ClickHouseContainer) ConnectionHost(ctx context.Context) (string, error) { - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - port, err := c.MappedPort(ctx, nativePort) - if err != nil { - return "", err - } - - return host + ":" + port.Port(), nil + return c.PortEndpoint(ctx, nativePort, "") } // ConnectionString returns the dsn string for the clickhouse container, using the default, native 9000 port, and diff --git a/modules/consul/consul.go b/modules/consul/consul.go index e09fdc12f4..29243bfce4 100644 --- a/modules/consul/consul.go +++ b/modules/consul/consul.go @@ -27,18 +27,7 @@ type ConsulContainer struct { // //nolint:revive,staticcheck //FIXME func (c *ConsulContainer) ApiEndpoint(ctx context.Context) (string, error) { - mappedPort, err := c.MappedPort(ctx, defaultHTTPAPIPort) - if err != nil { - return "", err - } - - hostIP, err := c.Host(ctx) - if err != nil { - return "", err - } - - uri := fmt.Sprintf("%s:%s", hostIP, mappedPort.Port()) - return uri, nil + return c.PortEndpoint(ctx, defaultHTTPAPIPort, "") } // WithConfigString takes in a JSON string of keys and values to define a configuration to be used by the instance. diff --git a/modules/couchbase/couchbase.go b/modules/couchbase/couchbase.go index 2daa94b131..eddbd88de7 100644 --- a/modules/couchbase/couchbase.go +++ b/modules/couchbase/couchbase.go @@ -164,17 +164,7 @@ func StartContainer(ctx context.Context, opts ...Option) (*CouchbaseContainer, e // ConnectionString returns the connection string to connect to the Couchbase container instance. // It returns a string with the format couchbase://: func (c *CouchbaseContainer) ConnectionString(ctx context.Context) (string, error) { - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - port, err := c.MappedPort(ctx, KV_PORT) - if err != nil { - return "", err - } - - return fmt.Sprintf("couchbase://%s:%d", host, port.Int()), nil + return c.PortEndpoint(ctx, KV_PORT, "couchbase") } // Username returns the username of the Couchbase administrator. @@ -607,17 +597,11 @@ func (c *CouchbaseContainer) doHTTPRequest(ctx context.Context, port, path, meth } func (c *CouchbaseContainer) getURL(ctx context.Context, port, path string) (string, error) { - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - mappedPort, err := c.MappedPort(ctx, nat.Port(port)) + endpoint, err := c.PortEndpoint(ctx, nat.Port(port), "http") if err != nil { return "", err } - - return fmt.Sprintf("http://%s:%d%s", host, mappedPort.Int(), path), nil + return endpoint + path, nil } func (c *CouchbaseContainer) getInternalIPAddress(ctx context.Context) (string, error) { diff --git a/modules/databend/databend.go b/modules/databend/databend.go index 85202bbe44..e702a727dc 100644 --- a/modules/databend/databend.go +++ b/modules/databend/databend.go @@ -93,14 +93,9 @@ func (c *DatabendContainer) MustConnectionString(ctx context.Context, args ...st } func (c *DatabendContainer) ConnectionString(ctx context.Context, args ...string) (string, error) { - containerPort, err := c.MappedPort(ctx, "8000/tcp") + endpoint, err := c.PortEndpoint(ctx, "8000/tcp", "") if err != nil { - return "", fmt.Errorf("mapped port: %w", err) - } - - host, err := c.Host(ctx) - if err != nil { - return "", err + return "", fmt.Errorf("port endpoint: %w", err) } extraArgs := "" @@ -112,7 +107,7 @@ func (c *DatabendContainer) ConnectionString(ctx context.Context, args ...string } // databend://databend:databend@localhost:8000/default?sslmode=disable - connectionString := fmt.Sprintf("databend://%s:%s@%s:%s/%s%s", c.username, c.password, host, containerPort.Port(), c.database, extraArgs) + connectionString := fmt.Sprintf("databend://%s:%s@%s/%s%s", c.username, c.password, endpoint, c.database, extraArgs) return connectionString, nil } diff --git a/modules/dolt/dolt.go b/modules/dolt/dolt.go index 62e103605a..9e8b4d9812 100644 --- a/modules/dolt/dolt.go +++ b/modules/dolt/dolt.go @@ -152,17 +152,12 @@ func (c *DoltContainer) initialize(ctx context.Context, createUser bool) error { } func (c *DoltContainer) initialConnectionString(ctx context.Context) (string, error) { - containerPort, err := c.MappedPort(ctx, "3306/tcp") + endpoint, err := c.PortEndpoint(ctx, "3306/tcp", "") if err != nil { return "", err } - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - connectionString := fmt.Sprintf("root:@tcp(%s:%s)/", host, containerPort.Port()) + connectionString := fmt.Sprintf("root:@tcp(%s)/", endpoint) return connectionString, nil } @@ -175,12 +170,7 @@ func (c *DoltContainer) MustConnectionString(ctx context.Context, args ...string } func (c *DoltContainer) ConnectionString(ctx context.Context, args ...string) (string, error) { - containerPort, err := c.MappedPort(ctx, "3306/tcp") - if err != nil { - return "", err - } - - host, err := c.Host(ctx) + endpoint, err := c.PortEndpoint(ctx, "3306/tcp", "") if err != nil { return "", err } @@ -193,7 +183,7 @@ func (c *DoltContainer) ConnectionString(ctx context.Context, args ...string) (s extraArgs = "?" + extraArgs } - connectionString := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s%s", c.username, c.password, host, containerPort.Port(), c.database, extraArgs) + connectionString := fmt.Sprintf("%s:%s@tcp(%s)/%s%s", c.username, c.password, endpoint, c.database, extraArgs) return connectionString, nil } diff --git a/modules/dynamodb/dynamodb.go b/modules/dynamodb/dynamodb.go index 62a6938efe..411fc53ccf 100644 --- a/modules/dynamodb/dynamodb.go +++ b/modules/dynamodb/dynamodb.go @@ -54,17 +54,7 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom // ConnectionString returns DynamoDB local endpoint host and port in : format func (c *DynamoDBContainer) ConnectionString(ctx context.Context) (string, error) { - mappedPort, err := c.MappedPort(ctx, port) - if err != nil { - return "", err - } - - hostIP, err := c.Host(ctx) - if err != nil { - return "", err - } - - return hostIP + ":" + mappedPort.Port(), nil + return c.PortEndpoint(ctx, port, "") } // WithSharedDB allows container reuse between successive runs. Data will be persisted diff --git a/modules/elasticsearch/elasticsearch.go b/modules/elasticsearch/elasticsearch.go index 33c65cc4b0..5b2979d6a9 100644 --- a/modules/elasticsearch/elasticsearch.go +++ b/modules/elasticsearch/elasticsearch.go @@ -159,22 +159,17 @@ func setWaitFor(options *Options, req *testcontainers.ContainerRequest) { // configureAddress sets the address of the Elasticsearch container. // If the certificate is set, it will use https as protocol, otherwise http. func (c *ElasticsearchContainer) configureAddress(ctx context.Context) error { - containerPort, err := c.MappedPort(ctx, defaultHTTPPort+"/tcp") - if err != nil { - return fmt.Errorf("mapped port: %w", err) - } - - host, err := c.Host(ctx) - if err != nil { - return fmt.Errorf("host: %w", err) - } - proto := "http" if c.Settings.CACert != nil { proto = "https" } - c.Settings.Address = fmt.Sprintf("%s://%s:%s", proto, host, containerPort.Port()) + endpoint, err := c.PortEndpoint(ctx, defaultHTTPPort+"/tcp", proto) + if err != nil { + return fmt.Errorf("port endpoint: %w", err) + } + + c.Settings.Address = endpoint return nil } diff --git a/modules/etcd/etcd.go b/modules/etcd/etcd.go index 42f0f88031..a1b87b0c79 100644 --- a/modules/etcd/etcd.go +++ b/modules/etcd/etcd.go @@ -206,17 +206,7 @@ func configureCMD(settings options) []string { // ClientEndpoint returns the client endpoint for the etcd container, and an error if any. // For a cluster, it returns the client endpoint of the first node. func (c *EtcdContainer) ClientEndpoint(ctx context.Context) (string, error) { - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - port, err := c.MappedPort(ctx, clientPort) - if err != nil { - return "", err - } - - return fmt.Sprintf("http://%s:%s", host, port.Port()), nil + return c.PortEndpoint(ctx, clientPort, "http") } // ClientEndpoints returns the client endpoints for the etcd cluster. @@ -242,17 +232,7 @@ func (c *EtcdContainer) ClientEndpoints(ctx context.Context) ([]string, error) { // PeerEndpoint returns the peer endpoint for the etcd container, and an error if any. // For a cluster, it returns the peer endpoint of the first node. func (c *EtcdContainer) PeerEndpoint(ctx context.Context) (string, error) { - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - port, err := c.MappedPort(ctx, peerPort) - if err != nil { - return "", err - } - - return fmt.Sprintf("http://%s:%s", host, port.Port()), nil + return c.PortEndpoint(ctx, peerPort, "http") } // PeerEndpoints returns the peer endpoints for the etcd cluster. diff --git a/modules/gcloud/bigquery.go b/modules/gcloud/bigquery.go index 1e99aafda9..5eabedddab 100644 --- a/modules/gcloud/bigquery.go +++ b/modules/gcloud/bigquery.go @@ -47,5 +47,5 @@ func RunBigQuery(ctx context.Context, img string, opts ...testcontainers.Contain }) } - return newGCloudContainer(ctx, req, 9050, settings, "http://") + return newGCloudContainer(ctx, req, 9050, settings, "http") } diff --git a/modules/gcloud/gcloud.go b/modules/gcloud/gcloud.go index 157bbf934f..c814b99e7b 100644 --- a/modules/gcloud/gcloud.go +++ b/modules/gcloud/gcloud.go @@ -27,7 +27,7 @@ type GCloudContainer struct { } // newGCloudContainer creates a new GCloud container, obtaining the URL to access the container from the specified port. -func newGCloudContainer(ctx context.Context, req testcontainers.GenericContainerRequest, port int, settings options, urlPrefix string) (*GCloudContainer, error) { +func newGCloudContainer(ctx context.Context, req testcontainers.GenericContainerRequest, port int, settings options, proto string) (*GCloudContainer, error) { container, err := testcontainers.GenericContainer(ctx, req) var c *GCloudContainer if container != nil { @@ -37,17 +37,12 @@ func newGCloudContainer(ctx context.Context, req testcontainers.GenericContainer return c, fmt.Errorf("generic container: %w", err) } - mappedPort, err := c.MappedPort(ctx, nat.Port(fmt.Sprintf("%d/tcp", port))) + endpoint, err := c.PortEndpoint(ctx, nat.Port(fmt.Sprintf("%d/tcp", port)), proto) if err != nil { - return c, fmt.Errorf("mapped port: %w", err) + return c, fmt.Errorf("port endpoint: %w", err) } - hostIP, err := c.Host(ctx) - if err != nil { - return c, fmt.Errorf("host: %w", err) - } - - c.URI = urlPrefix + hostIP + ":" + mappedPort.Port() + c.URI = endpoint return c, nil } diff --git a/modules/grafana-lgtm/go.mod b/modules/grafana-lgtm/go.mod index e4a7b3d474..351607411e 100644 --- a/modules/grafana-lgtm/go.mod +++ b/modules/grafana-lgtm/go.mod @@ -5,7 +5,6 @@ go 1.23.0 toolchain go1.23.6 require ( - github.com/docker/go-connections v0.5.0 github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.37.0 go.opentelemetry.io/contrib/bridges/otelslog v0.3.0 @@ -39,6 +38,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v28.2.2+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/ebitengine/purego v0.8.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect diff --git a/modules/grafana-lgtm/grafana.go b/modules/grafana-lgtm/grafana.go index ee9eeea46f..46a0f8d543 100644 --- a/modules/grafana-lgtm/grafana.go +++ b/modules/grafana-lgtm/grafana.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - "github.com/docker/go-connections/nat" - "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/log" "github.com/testcontainers/testcontainers-go/wait" @@ -72,7 +70,7 @@ func WithAdminCredentials(user, password string) testcontainers.ContainerCustomi // LokiEndpoint returns the Loki endpoint func (c *GrafanaLGTMContainer) LokiEndpoint(ctx context.Context) (string, error) { - url, err := baseEndpoint(ctx, c, LokiPort) + url, err := c.PortEndpoint(ctx, LokiPort, "") if err != nil { return "", err } @@ -92,7 +90,7 @@ func (c *GrafanaLGTMContainer) MustLokiEndpoint(ctx context.Context) string { // TempoEndpoint returns the Tempo endpoint func (c *GrafanaLGTMContainer) TempoEndpoint(ctx context.Context) (string, error) { - url, err := baseEndpoint(ctx, c, TempoPort) + url, err := c.PortEndpoint(ctx, TempoPort, "") if err != nil { return "", err } @@ -114,7 +112,7 @@ func (c *GrafanaLGTMContainer) MustTempoEndpoint(ctx context.Context) string { // //nolint:revive,staticcheck //FIXME func (c *GrafanaLGTMContainer) HttpEndpoint(ctx context.Context) (string, error) { - url, err := baseEndpoint(ctx, c, GrafanaPort) + url, err := c.PortEndpoint(ctx, GrafanaPort, "") if err != nil { return "", err } @@ -138,7 +136,7 @@ func (c *GrafanaLGTMContainer) MustHttpEndpoint(ctx context.Context) string { // //nolint:revive,staticcheck //FIXME func (c *GrafanaLGTMContainer) OtlpHttpEndpoint(ctx context.Context) (string, error) { - url, err := baseEndpoint(ctx, c, OtlpHttpPort) + url, err := c.PortEndpoint(ctx, OtlpHttpPort, "") if err != nil { return "", err } @@ -160,7 +158,7 @@ func (c *GrafanaLGTMContainer) MustOtlpHttpEndpoint(ctx context.Context) string // OtlpGrpcEndpoint returns the OTLP gRPC endpoint func (c *GrafanaLGTMContainer) OtlpGrpcEndpoint(ctx context.Context) (string, error) { - url, err := baseEndpoint(ctx, c, OtlpGrpcPort) + url, err := c.PortEndpoint(ctx, OtlpGrpcPort, "") if err != nil { return "", err } @@ -182,7 +180,7 @@ func (c *GrafanaLGTMContainer) MustOtlpGrpcEndpoint(ctx context.Context) string // //nolint:revive,staticcheck //FIXME func (c *GrafanaLGTMContainer) PrometheusHttpEndpoint(ctx context.Context) (string, error) { - url, err := baseEndpoint(ctx, c, PrometheusPort) + url, err := c.PortEndpoint(ctx, PrometheusPort, "") if err != nil { return "", err } @@ -201,17 +199,3 @@ func (c *GrafanaLGTMContainer) MustPrometheusHttpEndpoint(ctx context.Context) s return url } - -func baseEndpoint(ctx context.Context, c *GrafanaLGTMContainer, port nat.Port) (string, error) { - host, err := c.Host(ctx) - if err != nil { - return "", err - } - - mappedPort, err := c.MappedPort(ctx, port) - if err != nil { - return "", err - } - - return fmt.Sprintf("%s:%s", host, mappedPort.Port()), nil -} diff --git a/modules/k3s/k3s.go b/modules/k3s/k3s.go index 21729c0ea5..c00ad14bd7 100644 --- a/modules/k3s/k3s.go +++ b/modules/k3s/k3s.go @@ -17,7 +17,7 @@ import ( "github.com/testcontainers/testcontainers-go/wait" ) -var ( +const ( // containerPorts { defaultKubeSecurePort = "6443/tcp" defaultRancherWebhookPort = "8443/tcp" @@ -139,16 +139,6 @@ func getContainerHost(ctx context.Context, opts ...testcontainers.ContainerCusto // GetKubeConfig returns the modified kubeconfig with server url func (c *K3sContainer) GetKubeConfig(ctx context.Context) ([]byte, error) { - hostIP, err := c.Host(ctx) - if err != nil { - return nil, fmt.Errorf("failed to get hostIP: %w", err) - } - - mappedPort, err := c.MappedPort(ctx, nat.Port(defaultKubeSecurePort)) - if err != nil { - return nil, fmt.Errorf("failed to get mapped port: %w", err) - } - reader, err := c.CopyFileFromContainer(ctx, defaultKubeConfigK3sPath) if err != nil { return nil, fmt.Errorf("failed to copy file from container: %w", err) @@ -159,7 +149,11 @@ func (c *K3sContainer) GetKubeConfig(ctx context.Context) ([]byte, error) { return nil, fmt.Errorf("failed to read file from container: %w", err) } - server := "https://" + fmt.Sprintf("%v:%d", hostIP, mappedPort.Int()) + server, err := c.PortEndpoint(ctx, nat.Port(defaultKubeSecurePort), "https") + if err != nil { + return nil, fmt.Errorf("failed to get port endpoint: %w", err) + } + newKubeConfig, err := kubeConfigWithServerURL(string(kubeConfigYaml), server) if err != nil { return nil, fmt.Errorf("failed to modify kubeconfig with server url: %w", err) diff --git a/modules/ollama/local.go b/modules/ollama/local.go index abb317fde3..20226ca891 100644 --- a/modules/ollama/local.go +++ b/modules/ollama/local.go @@ -634,8 +634,8 @@ func (c *localProcess) NetworkAliases(_ context.Context) (map[string][]string, e } // PortEndpoint implements testcontainers.Container interface for the local Ollama binary. -// It returns proto://host:port string for the given exposed port. -// It returns just host:port if proto is blank. +// It returns proto://host:port or proto://[IPv6host]:port string for the given exposed port. +// It returns just host:port or [IPv6host]:port if proto is blank. func (c *localProcess) PortEndpoint(ctx context.Context, port nat.Port, proto string) (string, error) { host, err := c.Host(ctx) if err != nil { @@ -647,11 +647,11 @@ func (c *localProcess) PortEndpoint(ctx context.Context, port nat.Port, proto st return "", fmt.Errorf("mapped port: %w", err) } - if proto != "" { - proto += "://" + hostPost := net.JoinHostPort(host, outerPort.Port()) + if proto == "" { + return hostPost, nil } - - return fmt.Sprintf("%s%s:%s", proto, host, outerPort.Port()), nil + return proto + "://" + hostPost, nil } // SessionID implements testcontainers.Container interface for the local Ollama binary. diff --git a/modules/redis/redis.go b/modules/redis/redis.go index 517a2bd44f..defe86605b 100644 --- a/modules/redis/redis.go +++ b/modules/redis/redis.go @@ -36,23 +36,11 @@ type RedisContainer struct { // ConnectionString returns the connection string for the Redis container. // It uses the default 6379 port. func (c *RedisContainer) ConnectionString(ctx context.Context) (string, error) { - mappedPort, err := c.MappedPort(ctx, redisPort) - if err != nil { - return "", err - } - - hostIP, err := c.Host(ctx) - if err != nil { - return "", err - } - schema := "redis" if c.settings.tlsEnabled { schema = "rediss" } - - uri := fmt.Sprintf("%s://%s:%s", schema, hostIP, mappedPort.Port()) - return uri, nil + return c.PortEndpoint(ctx, redisPort, schema) } // TLSConfig returns the TLS configuration for the Redis container, nil if TLS is not enabled.