From ac22f514f38133b2f03830339efa0cf7da0f07e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 2 Dec 2022 00:19:57 +0100 Subject: [PATCH 01/10] feat: support passing registry credentials to the reaper --- reaper.go | 39 +++++++++++++++++++++++++++++++-------- reaper_test.go | 20 +++++++++++++++----- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/reaper.go b/reaper.go index a82a8ba54f..1d9c24a5f0 100644 --- a/reaper.go +++ b/reaper.go @@ -39,8 +39,23 @@ type ReaperProvider interface { Config() TestContainersConfig } +// ReaperOptions functional options for the reaper +type reaperOptions struct { + RegistryCredentials string +} + +// functional option for setting the reaper image +type ReaperOption func(*reaperOptions) + +// WithRegistryCredentials sets the reaper registry credentials +func WithRegistryCredentials(registryCredentials string) ReaperOption { + return func(o *reaperOptions) { + o.RegistryCredentials = registryCredentials + } +} + // NewReaper creates a Reaper with a sessionID to identify containers and a provider to use -func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, reaperImageName string) (*Reaper, error) { +func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, reaperImageName string, opts ...ReaperOption) (*Reaper, error) { mutex.Lock() defer mutex.Unlock() // If reaper already exists re-use it @@ -58,6 +73,12 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, r listeningPort := nat.Port("8080/tcp") + reaperOpts := reaperOptions{} + + for _, opt := range opts { + opt(&reaperOpts) + } + req := ContainerRequest{ Image: reaperImage(reaperImageName), ExposedPorts: []string{string(listeningPort)}, @@ -65,10 +86,11 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, r Labels: map[string]string{ TestcontainerLabelIsReaper: "true", }, - SkipReaper: true, - Mounts: Mounts(BindMount(dockerHost, "/var/run/docker.sock")), - AutoRemove: true, - WaitingFor: wait.ForListeningPort(listeningPort), + SkipReaper: true, + RegistryCred: reaperOpts.RegistryCredentials, + Mounts: Mounts(BindMount(dockerHost, "/var/run/docker.sock")), + AutoRemove: true, + WaitingFor: wait.ForListeningPort(listeningPort), } // include reaper-specific labels to the reaper container @@ -100,9 +122,10 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, r // Reaper is used to start a sidecar container that cleans up resources type Reaper struct { - Provider ReaperProvider - SessionID string - Endpoint string + Provider ReaperProvider + SessionID string + Endpoint string + registryCredentials string } // Connect runs a goroutine which can be terminated by sending true into the returned channel diff --git a/reaper_test.go b/reaper_test.go index fd22fff377..96e1c5d668 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -55,10 +55,11 @@ func createContainerRequest(customize func(ContainerRequest) ContainerRequest) C func Test_NewReaper(t *testing.T) { type cases struct { - name string - req ContainerRequest - config TestContainersConfig - ctx context.Context + name string + req ContainerRequest + config TestContainersConfig + ctx context.Context + reaperOptions []ReaperOption } tests := []cases{ @@ -86,6 +87,15 @@ func Test_NewReaper(t *testing.T) { config: TestContainersConfig{}, ctx: context.WithValue(context.TODO(), dockerHostContextKey, "unix:///value/in/context.sock"), }, + { + name: "with registry credentials", + req: createContainerRequest(func(req ContainerRequest) ContainerRequest { + req.RegistryCred = "registry-cred" + return req + }), + config: TestContainersConfig{}, + reaperOptions: []ReaperOption{WithRegistryCredentials("registry-cred")}, + }, } for _, test := range tests { @@ -100,7 +110,7 @@ func Test_NewReaper(t *testing.T) { test.ctx = context.TODO() } - _, err := NewReaper(test.ctx, "sessionId", provider, "reaperImage") + _, err := NewReaper(test.ctx, "sessionId", provider, "reaperImage", test.reaperOptions...) // we should have errored out see mockReaperProvider.RunContainer assert.EqualError(t, err, "expected") From 4847eb50b63b0a8cf43fba273bed759d13e85813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 2 Dec 2022 00:49:45 +0100 Subject: [PATCH 02/10] chore: support passing registry creds for the reaper when creating networks --- docker.go | 2 +- network.go | 6 ++++-- reaper_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docker.go b/docker.go index 9618288465..98b84908e1 100644 --- a/docker.go +++ b/docker.go @@ -1336,7 +1336,7 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) var termSignal chan bool if !req.SkipReaper { sessionID := sessionID() - r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperImage) + r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperImage, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating network reaper failed", err) } diff --git a/network.go b/network.go index eac39a5cad..3d44c77c45 100644 --- a/network.go +++ b/network.go @@ -2,6 +2,7 @@ package testcontainers import ( "context" + "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types" @@ -39,6 +40,7 @@ type NetworkRequest struct { Attachable bool IPAM *network.IPAM - SkipReaper bool // indicates whether we skip setting up a reaper for this - ReaperImage string //alternative reaper registry + SkipReaper bool // indicates whether we skip setting up a reaper for this + ReaperImage string // alternative reaper registry + ReaperOptions []ReaperOption // Reaper options to use for this network } diff --git a/reaper_test.go b/reaper_test.go index 96e1c5d668..c532d4dbcc 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -157,3 +157,28 @@ func Test_ExtractDockerHost(t *testing.T) { assert.Equal(t, "/this/is/a/sample.sock", host) }) } + +func Test_ReaperForNetwork(t *testing.T) { + ctx := context.Background() + + networkName := "test-network-with-custom-reaper" + + req := GenericNetworkRequest{ + NetworkRequest: NetworkRequest{ + Name: networkName, + CheckDuplicate: true, + ReaperOptions: []ReaperOption{ + WithRegistryCredentials("credentials"), + }, + }, + } + + provider := &mockReaperProvider{ + config: TestContainersConfig{}, + } + + _, err := NewReaper(ctx, "sessionId", provider, "reaperImage", req.ReaperOptions...) + assert.EqualError(t, err, "expected") + + assert.Equal(t, "credentials", provider.req.RegistryCred) +} From 9554fc39a2cca266e33991e09883ae0d6796b030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 2 Dec 2022 01:25:56 +0100 Subject: [PATCH 03/10] chore: support configuring the reaper as functional opts --- container.go | 3 ++- docker.go | 6 +++--- network.go | 2 +- reaper.go | 26 +++++++++++++++++++------- reaper_test.go | 32 ++++++++++++++++++++++---------- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/container.go b/container.go index dc03eb82ed..dd73a531ca 100644 --- a/container.go +++ b/container.go @@ -115,7 +115,8 @@ type ContainerRequest struct { Files []ContainerFile // files which will be copied when container starts User string // for specifying uid:gid SkipReaper bool // indicates whether we skip setting up a reaper for this - ReaperImage string // alternative reaper image + ReaperImage string // Deprecated: use WithImageName ReaperOption instead. Alternative reaper image + ReaperOptions []ReaperOption // options for the reaper AutoRemove bool // if set to true, the container will be removed from the host when stopped AlwaysPullImage bool // Always pull image ImagePlatform string // ImagePlatform describes the platform which the image runs on. diff --git a/docker.go b/docker.go index 98b84908e1..92c431bb1e 100644 --- a/docker.go +++ b/docker.go @@ -964,7 +964,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque // the reaper does not need to start a reaper for itself isReaperContainer := strings.EqualFold(req.Image, reaperImage(req.ReaperImage)) if !req.SkipReaper && !isReaperContainer { - r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperImage) + r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating reaper failed", err) } @@ -1181,7 +1181,7 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain sessionID := sessionID() var termSignal chan bool if !req.SkipReaper { - r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperImage) + r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating reaper failed", err) } @@ -1336,7 +1336,7 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) var termSignal chan bool if !req.SkipReaper { sessionID := sessionID() - r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperImage, req.ReaperOptions...) + r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating network reaper failed", err) } diff --git a/network.go b/network.go index 3d44c77c45..e3dc3c17a7 100644 --- a/network.go +++ b/network.go @@ -41,6 +41,6 @@ type NetworkRequest struct { IPAM *network.IPAM SkipReaper bool // indicates whether we skip setting up a reaper for this - ReaperImage string // alternative reaper registry + ReaperImage string // Deprecated: use WithImageName ReaperOption instead. Alternative reaper registry ReaperOptions []ReaperOption // Reaper options to use for this network } diff --git a/reaper.go b/reaper.go index 1d9c24a5f0..52580958c5 100644 --- a/reaper.go +++ b/reaper.go @@ -41,12 +41,20 @@ type ReaperProvider interface { // ReaperOptions functional options for the reaper type reaperOptions struct { + ImageName string RegistryCredentials string } // functional option for setting the reaper image type ReaperOption func(*reaperOptions) +// WithImageName sets the reaper image name +func WithImageName(imageName string) ReaperOption { + return func(o *reaperOptions) { + o.ImageName = imageName + } +} + // WithRegistryCredentials sets the reaper registry credentials func WithRegistryCredentials(registryCredentials string) ReaperOption { return func(o *reaperOptions) { @@ -55,7 +63,7 @@ func WithRegistryCredentials(registryCredentials string) ReaperOption { } // NewReaper creates a Reaper with a sessionID to identify containers and a provider to use -func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, reaperImageName string, opts ...ReaperOption) (*Reaper, error) { +func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, opts ...ReaperOption) (*Reaper, error) { mutex.Lock() defer mutex.Unlock() // If reaper already exists re-use it @@ -80,19 +88,23 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, r } req := ContainerRequest{ - Image: reaperImage(reaperImageName), + Image: reaperImage(reaperOpts.ImageName), ExposedPorts: []string{string(listeningPort)}, NetworkMode: Bridge, Labels: map[string]string{ TestcontainerLabelIsReaper: "true", }, - SkipReaper: true, - RegistryCred: reaperOpts.RegistryCredentials, - Mounts: Mounts(BindMount(dockerHost, "/var/run/docker.sock")), - AutoRemove: true, - WaitingFor: wait.ForListeningPort(listeningPort), + SkipReaper: true, + RegistryCred: reaperOpts.RegistryCredentials, + Mounts: Mounts(BindMount(dockerHost, "/var/run/docker.sock")), + AutoRemove: true, + WaitingFor: wait.ForListeningPort(listeningPort), + ReaperOptions: opts, } + // keep backwards compatibility + req.ReaperImage = req.Image + // include reaper-specific labels to the reaper container for k, v := range reaper.Labels() { req.Labels[k] = v diff --git a/reaper_test.go b/reaper_test.go index c532d4dbcc..e3b2faee4b 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -33,6 +33,7 @@ func (m *mockReaperProvider) Config() TestContainersConfig { func createContainerRequest(customize func(ContainerRequest) ContainerRequest) ContainerRequest { req := ContainerRequest{ Image: "reaperImage", + ReaperImage: "reaperImage", ExposedPorts: []string{"8080/tcp"}, Labels: map[string]string{ TestcontainerLabel: "true", @@ -44,6 +45,9 @@ func createContainerRequest(customize func(ContainerRequest) ContainerRequest) C AutoRemove: true, WaitingFor: wait.ForListeningPort(nat.Port("8080/tcp")), NetworkMode: "bridge", + ReaperOptions: []ReaperOption{ + WithImageName("reaperImage"), + }, } if customize == nil { return req @@ -53,13 +57,13 @@ func createContainerRequest(customize func(ContainerRequest) ContainerRequest) C } func Test_NewReaper(t *testing.T) { + defer func() { reaper = nil }() type cases struct { - name string - req ContainerRequest - config TestContainersConfig - ctx context.Context - reaperOptions []ReaperOption + name string + req ContainerRequest + config TestContainersConfig + ctx context.Context } tests := []cases{ @@ -90,11 +94,12 @@ func Test_NewReaper(t *testing.T) { { name: "with registry credentials", req: createContainerRequest(func(req ContainerRequest) ContainerRequest { - req.RegistryCred = "registry-cred" + creds := "registry-creds" + req.RegistryCred = creds + req.ReaperOptions = append(req.ReaperOptions, WithRegistryCredentials(creds)) return req }), - config: TestContainersConfig{}, - reaperOptions: []ReaperOption{WithRegistryCredentials("registry-cred")}, + config: TestContainersConfig{}, }, } @@ -110,7 +115,7 @@ func Test_NewReaper(t *testing.T) { test.ctx = context.TODO() } - _, err := NewReaper(test.ctx, "sessionId", provider, "reaperImage", test.reaperOptions...) + _, err := NewReaper(test.ctx, "sessionId", provider, test.req.ReaperOptions...) // we should have errored out see mockReaperProvider.RunContainer assert.EqualError(t, err, "expected") @@ -120,6 +125,8 @@ func Test_NewReaper(t *testing.T) { } func Test_ExtractDockerHost(t *testing.T) { + defer func() { reaper = nil }() + t.Run("Docker Host as environment variable", func(t *testing.T) { t.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", "/path/to/docker.sock") host := extractDockerHost(context.Background()) @@ -159,6 +166,8 @@ func Test_ExtractDockerHost(t *testing.T) { } func Test_ReaperForNetwork(t *testing.T) { + defer func() { reaper = nil }() + ctx := context.Background() networkName := "test-network-with-custom-reaper" @@ -169,6 +178,7 @@ func Test_ReaperForNetwork(t *testing.T) { CheckDuplicate: true, ReaperOptions: []ReaperOption{ WithRegistryCredentials("credentials"), + WithImageName("reaperImage"), }, }, } @@ -177,8 +187,10 @@ func Test_ReaperForNetwork(t *testing.T) { config: TestContainersConfig{}, } - _, err := NewReaper(ctx, "sessionId", provider, "reaperImage", req.ReaperOptions...) + _, err := NewReaper(ctx, "sessionId", provider, req.ReaperOptions...) assert.EqualError(t, err, "expected") assert.Equal(t, "credentials", provider.req.RegistryCred) + assert.Equal(t, "reaperImage", provider.req.Image) + assert.Equal(t, "reaperImage", provider.req.ReaperImage) } From 0fe867150c86d59fe944972783e2de14735129e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 2 Dec 2022 08:03:10 +0100 Subject: [PATCH 04/10] chore: remove unused field --- reaper.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/reaper.go b/reaper.go index 52580958c5..5c860e829a 100644 --- a/reaper.go +++ b/reaper.go @@ -134,10 +134,9 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, o // Reaper is used to start a sidecar container that cleans up resources type Reaper struct { - Provider ReaperProvider - SessionID string - Endpoint string - registryCredentials string + Provider ReaperProvider + SessionID string + Endpoint string } // Connect runs a goroutine which can be terminated by sending true into the returned channel From e982bff52facd526ec2c9cbeffaa2741db190059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 2 Dec 2022 08:09:27 +0100 Subject: [PATCH 05/10] chore: deprecate public reaper constructor We do not want to create reapers from outside the library --- docker.go | 6 +++--- reaper.go | 8 +++++++- reaper_test.go | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docker.go b/docker.go index 92c431bb1e..0f42da4ecf 100644 --- a/docker.go +++ b/docker.go @@ -964,7 +964,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque // the reaper does not need to start a reaper for itself isReaperContainer := strings.EqualFold(req.Image, reaperImage(req.ReaperImage)) if !req.SkipReaper && !isReaperContainer { - r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) + r, err := newReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating reaper failed", err) } @@ -1181,7 +1181,7 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain sessionID := sessionID() var termSignal chan bool if !req.SkipReaper { - r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) + r, err := newReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating reaper failed", err) } @@ -1336,7 +1336,7 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) var termSignal chan bool if !req.SkipReaper { sessionID := sessionID() - r, err := NewReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) + r, err := newReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) if err != nil { return nil, fmt.Errorf("%w: creating network reaper failed", err) } diff --git a/reaper.go b/reaper.go index 5c860e829a..d9615b738f 100644 --- a/reaper.go +++ b/reaper.go @@ -63,7 +63,13 @@ func WithRegistryCredentials(registryCredentials string) ReaperOption { } // NewReaper creates a Reaper with a sessionID to identify containers and a provider to use -func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, opts ...ReaperOption) (*Reaper, error) { +// Deprecated: it's not possible to create a reaper anymore. +func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, reaperImageName string) (*Reaper, error) { + return newReaper(ctx, sessionID, provider, WithImageName(reaperImageName)) +} + +// newReaper creates a Reaper with a sessionID to identify containers and a provider to use +func newReaper(ctx context.Context, sessionID string, provider ReaperProvider, opts ...ReaperOption) (*Reaper, error) { mutex.Lock() defer mutex.Unlock() // If reaper already exists re-use it diff --git a/reaper_test.go b/reaper_test.go index e3b2faee4b..f97dc5e61a 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -115,7 +115,7 @@ func Test_NewReaper(t *testing.T) { test.ctx = context.TODO() } - _, err := NewReaper(test.ctx, "sessionId", provider, test.req.ReaperOptions...) + _, err := newReaper(test.ctx, "sessionId", provider, test.req.ReaperOptions...) // we should have errored out see mockReaperProvider.RunContainer assert.EqualError(t, err, "expected") @@ -187,7 +187,7 @@ func Test_ReaperForNetwork(t *testing.T) { config: TestContainersConfig{}, } - _, err := NewReaper(ctx, "sessionId", provider, req.ReaperOptions...) + _, err := newReaper(ctx, "sessionId", provider, req.ReaperOptions...) assert.EqualError(t, err, "expected") assert.Equal(t, "credentials", provider.req.RegistryCred) From 1467a583d2f7c9e6de4fbb030f727240ab8f03f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 Dec 2022 08:35:38 +0100 Subject: [PATCH 06/10] chore: rename ReaperOption to ContainerOption This will provide a better namespace for future work --- container.go | 39 +++++++++++++++++++++++++++++++-------- network.go | 6 +++--- reaper.go | 27 ++------------------------- reaper_test.go | 4 ++-- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/container.go b/container.go index dd73a531ca..c23dfef228 100644 --- a/container.go +++ b/container.go @@ -112,14 +112,14 @@ type ContainerRequest struct { NetworkAliases map[string][]string // for specifying network aliases NetworkMode container.NetworkMode Resources container.Resources - Files []ContainerFile // files which will be copied when container starts - User string // for specifying uid:gid - SkipReaper bool // indicates whether we skip setting up a reaper for this - ReaperImage string // Deprecated: use WithImageName ReaperOption instead. Alternative reaper image - ReaperOptions []ReaperOption // options for the reaper - AutoRemove bool // if set to true, the container will be removed from the host when stopped - AlwaysPullImage bool // Always pull image - ImagePlatform string // ImagePlatform describes the platform which the image runs on. + Files []ContainerFile // files which will be copied when container starts + User string // for specifying uid:gid + SkipReaper bool // indicates whether we skip setting up a reaper for this + ReaperImage string // Deprecated: use WithImageName ContainerOption instead. Alternative reaper image + ReaperOptions []ContainerOption // options for the reaper + AutoRemove bool // if set to true, the container will be removed from the host when stopped + AlwaysPullImage bool // Always pull image + ImagePlatform string // ImagePlatform describes the platform which the image runs on. Binds []string ShmSize int64 // Amount of memory shared with the host (in bytes) CapAdd []string // Add Linux capabilities @@ -150,6 +150,29 @@ func (f GenericProviderOptionFunc) ApplyGenericTo(opts *GenericProviderOptions) f(opts) } +// containerOptions functional options for a container +type containerOptions struct { + ImageName string + RegistryCredentials string +} + +// functional option for setting the reaper image +type ContainerOption func(*containerOptions) + +// WithImageName sets the reaper image name +func WithImageName(imageName string) ContainerOption { + return func(o *containerOptions) { + o.ImageName = imageName + } +} + +// WithRegistryCredentials sets the reaper registry credentials +func WithRegistryCredentials(registryCredentials string) ContainerOption { + return func(o *containerOptions) { + o.RegistryCredentials = registryCredentials + } +} + // possible provider types const ( ProviderDocker ProviderType = iota // Docker is default = 0 diff --git a/network.go b/network.go index e3dc3c17a7..e8436240fc 100644 --- a/network.go +++ b/network.go @@ -40,7 +40,7 @@ type NetworkRequest struct { Attachable bool IPAM *network.IPAM - SkipReaper bool // indicates whether we skip setting up a reaper for this - ReaperImage string // Deprecated: use WithImageName ReaperOption instead. Alternative reaper registry - ReaperOptions []ReaperOption // Reaper options to use for this network + SkipReaper bool // indicates whether we skip setting up a reaper for this + ReaperImage string // Deprecated: use WithImageName ContainerOption instead. Alternative reaper registry + ReaperOptions []ContainerOption // Reaper options to use for this network } diff --git a/reaper.go b/reaper.go index d9615b738f..0cc5146e36 100644 --- a/reaper.go +++ b/reaper.go @@ -39,29 +39,6 @@ type ReaperProvider interface { Config() TestContainersConfig } -// ReaperOptions functional options for the reaper -type reaperOptions struct { - ImageName string - RegistryCredentials string -} - -// functional option for setting the reaper image -type ReaperOption func(*reaperOptions) - -// WithImageName sets the reaper image name -func WithImageName(imageName string) ReaperOption { - return func(o *reaperOptions) { - o.ImageName = imageName - } -} - -// WithRegistryCredentials sets the reaper registry credentials -func WithRegistryCredentials(registryCredentials string) ReaperOption { - return func(o *reaperOptions) { - o.RegistryCredentials = registryCredentials - } -} - // NewReaper creates a Reaper with a sessionID to identify containers and a provider to use // Deprecated: it's not possible to create a reaper anymore. func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, reaperImageName string) (*Reaper, error) { @@ -69,7 +46,7 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, r } // newReaper creates a Reaper with a sessionID to identify containers and a provider to use -func newReaper(ctx context.Context, sessionID string, provider ReaperProvider, opts ...ReaperOption) (*Reaper, error) { +func newReaper(ctx context.Context, sessionID string, provider ReaperProvider, opts ...ContainerOption) (*Reaper, error) { mutex.Lock() defer mutex.Unlock() // If reaper already exists re-use it @@ -87,7 +64,7 @@ func newReaper(ctx context.Context, sessionID string, provider ReaperProvider, o listeningPort := nat.Port("8080/tcp") - reaperOpts := reaperOptions{} + reaperOpts := containerOptions{} for _, opt := range opts { opt(&reaperOpts) diff --git a/reaper_test.go b/reaper_test.go index f97dc5e61a..f90908dc0b 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -45,7 +45,7 @@ func createContainerRequest(customize func(ContainerRequest) ContainerRequest) C AutoRemove: true, WaitingFor: wait.ForListeningPort(nat.Port("8080/tcp")), NetworkMode: "bridge", - ReaperOptions: []ReaperOption{ + ReaperOptions: []ContainerOption{ WithImageName("reaperImage"), }, } @@ -176,7 +176,7 @@ func Test_ReaperForNetwork(t *testing.T) { NetworkRequest: NetworkRequest{ Name: networkName, CheckDuplicate: true, - ReaperOptions: []ReaperOption{ + ReaperOptions: []ContainerOption{ WithRegistryCredentials("credentials"), WithImageName("reaperImage"), }, From adddf05a07daf147c72cea8132bde83dd1634c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 23 Dec 2022 08:45:26 +0100 Subject: [PATCH 07/10] fix: extract reaper image before evaluating a new reaper --- docker.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker.go b/docker.go index 0f42da4ecf..5b10d3b68f 100644 --- a/docker.go +++ b/docker.go @@ -960,9 +960,14 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque sessionID := sessionID() + reaperOpts := containerOptions{} + for _, opt := range req.ReaperOptions { + opt(&reaperOpts) + } + var termSignal chan bool // the reaper does not need to start a reaper for itself - isReaperContainer := strings.EqualFold(req.Image, reaperImage(req.ReaperImage)) + isReaperContainer := strings.EqualFold(req.Image, reaperImage(reaperOpts.ImageName)) if !req.SkipReaper && !isReaperContainer { r, err := newReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...) if err != nil { From e1f37e4f1059d9bb0f3743b5f567803c7254178d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Sun, 1 Jan 2023 16:21:06 +0100 Subject: [PATCH 08/10] chore: fallback to req.ReaperImage Co-authored-by: Matthew McNew --- docker.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker.go b/docker.go index eadf22cc6e..ba5bd60411 100644 --- a/docker.go +++ b/docker.go @@ -961,7 +961,9 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque sessionID := sessionID() - reaperOpts := containerOptions{} + reaperOpts := containerOptions{ + ImageName: req.ReaperImage + } for _, opt := range req.ReaperOptions { opt(&reaperOpts) } From beca5f311c5772c9545861c0c7964fc321552dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Sun, 1 Jan 2023 16:23:54 +0100 Subject: [PATCH 09/10] fix: missing comma delimiting struct field --- docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker.go b/docker.go index ba5bd60411..2ea8508007 100644 --- a/docker.go +++ b/docker.go @@ -962,7 +962,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque sessionID := sessionID() reaperOpts := containerOptions{ - ImageName: req.ReaperImage + ImageName: req.ReaperImage, } for _, opt := range req.ReaperOptions { opt(&reaperOpts) From dc75db43425d3460fb272fd973be2d23e632ddcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 2 Jan 2023 11:03:53 +0100 Subject: [PATCH 10/10] fix: indent --- docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker.go b/docker.go index 2ea8508007..0767406a8c 100644 --- a/docker.go +++ b/docker.go @@ -962,7 +962,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque sessionID := sessionID() reaperOpts := containerOptions{ - ImageName: req.ReaperImage, + ImageName: req.ReaperImage, } for _, opt := range req.ReaperOptions { opt(&reaperOpts)