Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow explicitly specifying provider type #1990

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/system_requirements/using_podman.md
Expand Up @@ -35,6 +35,15 @@ func TestSomething(t *testing.T) {
}
```

If using the [Podman Desktop Docker compatibility mode](https://podman-desktop.io/docs/migrating-from-docker/using-podman-mac-helper),
where the `DOCKER_HOST` still points to the Docker socket,
you can also configure the provider explicitly in a `.testcontainers.properties` file
or the `TESTCONTAINERS_PROVIDER_TYPE` env variable.
```properties
provider.type=podman
```


The `ProviderPodman` configures the `DockerProvider` with the correct default network for Podman to ensure complex network scenarios are working as with Docker.

## Podman socket activation
Expand Down
3 changes: 3 additions & 0 deletions internal/config/config.go
Expand Up @@ -29,6 +29,7 @@ type Config struct {
RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"`
RyukReconnectionTimeout time.Duration `properties:"ryuk.reconnection.timeout,default=10s"`
RyukConnectionTimeout time.Duration `properties:"ryuk.connection.timeout,default=1m"`
ProviderType string `properties:"provider.type,default="`
TestcontainersHost string `properties:"tc.host,default="`
}

Expand Down Expand Up @@ -80,6 +81,8 @@ func read() Config {
config.RyukPrivileged = ryukPrivilegedEnv == "true"
}

config.ProviderType = os.Getenv("TESTCONTAINERS_PROVIDER_TYPE")

return config
}

Expand Down
31 changes: 27 additions & 4 deletions provider.go
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/testcontainers/testcontainers-go/internal/config"
"os"
"strings"

Expand Down Expand Up @@ -102,12 +103,21 @@ func (t ProviderType) GetProvider(opts ...GenericProviderOption) (GenericProvide
o.ApplyGenericTo(opt)
}

pt := t
if pt == ProviderDefault && strings.Contains(os.Getenv("DOCKER_HOST"), "podman.sock") {
pt = ProviderPodman
providerType := t
if providerType == ProviderDefault {
cfg := config.Read()
var err error
providerType, err = getProviderType(cfg.ProviderType)
if err != nil {
return nil, err
}
}

if providerType == ProviderDefault && strings.Contains(os.Getenv("DOCKER_HOST"), "podman.sock") {
providerType = ProviderPodman
}

switch pt {
switch providerType {
case ProviderDefault, ProviderDocker:
providerOptions := append(Generic2DockerOptions(opts...), WithDefaultBridgeNetwork(Bridge))
provider, err := NewDockerProvider(providerOptions...)
Expand Down Expand Up @@ -157,3 +167,16 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error

return p, nil
}

// getProviderType maps a human-readable string to a corresponding ProviderType.
func getProviderType(providerType string) (ProviderType, error) {
switch providerType {
case "":
return ProviderDefault, nil
case "docker":
return ProviderDocker, nil
case "podman":
return ProviderPodman, nil
}
return 0, errors.New(fmt.Sprintf("unknown provider: %s", providerType))
}
63 changes: 58 additions & 5 deletions provider_test.go
Expand Up @@ -2,6 +2,9 @@ package testcontainers

import (
"context"
"github.com/testcontainers/testcontainers-go/internal/config"
"os"
"path/filepath"
"testing"

"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
Expand All @@ -12,11 +15,12 @@ func TestProviderTypeGetProviderAutodetect(t *testing.T) {
const podmanSocket = "unix://$XDG_RUNTIME_DIR/podman/podman.sock"

tests := []struct {
name string
tr ProviderType
DockerHost string
want string
wantErr bool
name string
tr ProviderType
PropertiesProvider string
DockerHost string
want string
wantErr bool
}{
{
name: "default provider without podman.socket",
Expand Down Expand Up @@ -55,6 +59,21 @@ func TestProviderTypeGetProviderAutodetect(t *testing.T) {
DockerHost: podmanSocket,
want: Podman,
},
{
name: "default provider with podman configured in properties",
tr: ProviderDefault,
PropertiesProvider: "podman",
DockerHost: dockerHost,
want: Podman,
},
{
// Explicitly setting Docker provider should not be overridden by properties
name: "docker provider with podman configured in properties",
tr: ProviderDocker,
PropertiesProvider: "podman",
DockerHost: dockerHost,
want: Bridge,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -64,6 +83,8 @@ func TestProviderTypeGetProviderAutodetect(t *testing.T) {

t.Setenv("DOCKER_HOST", tt.DockerHost)

setupTestcontainersProperties(t, "provider.type="+tt.PropertiesProvider)

got, err := tt.tr.GetProvider()
if (err != nil) != tt.wantErr {
t.Errorf("ProviderType.GetProvider() error = %v, wantErr %v", err, tt.wantErr)
Expand All @@ -79,3 +100,35 @@ func TestProviderTypeGetProviderAutodetect(t *testing.T) {
})
}
}

func setupTestcontainersProperties(t *testing.T, content string) {
t.Cleanup(func() {
// reset the properties file after the test
config.Reset()
})

config.Reset()

tmpDir := t.TempDir()
homeDir := filepath.Join(tmpDir, "home")
err := createTmpDir(homeDir)
if err != nil {
t.Fatalf("failed to create tmp home dir: %v", err)
}
t.Setenv("HOME", homeDir)
t.Setenv("USERPROFILE", homeDir) // Windows support

if err := os.WriteFile(filepath.Join(homeDir, ".testcontainers.properties"), []byte(content), 0o600); err != nil {
t.Errorf("Failed to create the file: %v", err)
return
}
}

func createTmpDir(dir string) error {
err := os.MkdirAll(dir, 0o755)
if err != nil {
return err
}

return nil
}