package testcontainers import ( "context" "errors" "fmt" "os" "strings" "github.com/testcontainers/testcontainers-go/internal/testcontainersdocker" ) // possible provider types const ( ProviderDefault ProviderType = iota // default will auto-detect provider from DOCKER_HOST environment variable ProviderDocker ProviderPodman ) type ( // ProviderType is an enum for the possible providers ProviderType int // GenericProviderOptions defines options applicable to all providers GenericProviderOptions struct { Logger Logging DefaultNetwork string } // GenericProviderOption defines a common interface to modify GenericProviderOptions // These options can be passed to GetProvider in a variadic way to customize the returned GenericProvider instance GenericProviderOption interface { ApplyGenericTo(opts *GenericProviderOptions) } // GenericProviderOptionFunc is a shorthand to implement the GenericProviderOption interface GenericProviderOptionFunc func(opts *GenericProviderOptions) // DockerProviderOptions defines options applicable to DockerProvider DockerProviderOptions struct { defaultBridgeNetworkName string *GenericProviderOptions } // DockerProviderOption defines a common interface to modify DockerProviderOptions // These can be passed to NewDockerProvider in a variadic way to customize the returned DockerProvider instance DockerProviderOption interface { ApplyDockerTo(opts *DockerProviderOptions) } // DockerProviderOptionFunc is a shorthand to implement the DockerProviderOption interface DockerProviderOptionFunc func(opts *DockerProviderOptions) ) func (f DockerProviderOptionFunc) ApplyDockerTo(opts *DockerProviderOptions) { f(opts) } func Generic2DockerOptions(opts ...GenericProviderOption) []DockerProviderOption { converted := make([]DockerProviderOption, 0, len(opts)) for _, o := range opts { switch c := o.(type) { case DockerProviderOption: converted = append(converted, c) default: converted = append(converted, DockerProviderOptionFunc(func(opts *DockerProviderOptions) { o.ApplyGenericTo(opts.GenericProviderOptions) })) } } return converted } func WithDefaultBridgeNetwork(bridgeNetworkName string) DockerProviderOption { return DockerProviderOptionFunc(func(opts *DockerProviderOptions) { opts.defaultBridgeNetworkName = bridgeNetworkName }) } func (f GenericProviderOptionFunc) ApplyGenericTo(opts *GenericProviderOptions) { f(opts) } // ContainerProvider allows the creation of containers on an arbitrary system type ContainerProvider interface { Close() error // close the provider CreateContainer(context.Context, ContainerRequest) (Container, error) // create a container without starting it ReuseOrCreateContainer(context.Context, ContainerRequest) (Container, error) // reuses a container if it exists or creates a container without starting RunContainer(context.Context, ContainerRequest) (Container, error) // create a container and start it Health(context.Context) error Config() TestcontainersConfig } // GetProvider provides the provider implementation for a certain type func (t ProviderType) GetProvider(opts ...GenericProviderOption) (GenericProvider, error) { opt := &GenericProviderOptions{ Logger: Logger, } for _, o := range opts { o.ApplyGenericTo(opt) } pt := t if pt == ProviderDefault && strings.Contains(os.Getenv("DOCKER_HOST"), "podman.sock") { pt = ProviderPodman } switch pt { case ProviderDefault, ProviderDocker: providerOptions := append(Generic2DockerOptions(opts...), WithDefaultBridgeNetwork(Bridge)) provider, err := NewDockerProvider(providerOptions...) if err != nil { return nil, fmt.Errorf("%w, failed to create Docker provider", err) } return provider, nil case ProviderPodman: providerOptions := append(Generic2DockerOptions(opts...), WithDefaultBridgeNetwork(Podman)) provider, err := NewDockerProvider(providerOptions...) if err != nil { return nil, fmt.Errorf("%w, failed to create Docker provider", err) } return provider, nil } return nil, errors.New("unknown provider") } // NewDockerProvider creates a Docker provider with the EnvClient func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error) { o := &DockerProviderOptions{ GenericProviderOptions: &GenericProviderOptions{ Logger: Logger, }, } for idx := range provOpts { provOpts[idx].ApplyDockerTo(o) } c, err := NewDockerClient() if err != nil { return nil, err } tcConfig := ReadConfig() dockerHost := testcontainersdocker.ExtractDockerHost(context.Background()) p := &DockerProvider{ DockerProviderOptions: o, host: dockerHost, client: c, config: tcConfig, } // log docker server info only once logOnce.Do(func() { LogDockerServerInfo(context.Background(), p.client, p.Logger) }) return p, nil }