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

chore: add tests for withNetwork option #1894

Merged
merged 4 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 0 additions & 54 deletions container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,60 +467,6 @@ func TestShouldStartContainersInParallel(t *testing.T) {
}
}

func TestOverrideContainerRequest(t *testing.T) {
req := GenericContainerRequest{
ContainerRequest: ContainerRequest{
Env: map[string]string{
"BAR": "BAR",
},
Image: "foo",
ExposedPorts: []string{"12345/tcp"},
WaitingFor: wait.ForNop(
func(ctx context.Context, target wait.StrategyTarget) error {
return nil
},
),
Networks: []string{"foo", "bar", "baaz"},
NetworkAliases: map[string][]string{
"foo": {"foo0", "foo1", "foo2", "foo3"},
},
},
}

toBeMergedRequest := GenericContainerRequest{
ContainerRequest: ContainerRequest{
Env: map[string]string{
"FOO": "FOO",
},
Image: "bar",
ExposedPorts: []string{"67890/tcp"},
Networks: []string{"foo1", "bar1"},
NetworkAliases: map[string][]string{
"foo1": {"bar"},
},
WaitingFor: wait.ForLog("foo"),
},
}

// the toBeMergedRequest should be merged into the req
CustomizeRequest(toBeMergedRequest)(&req)

// toBeMergedRequest should not be changed
assert.Equal(t, "", toBeMergedRequest.Env["BAR"])
assert.Equal(t, 1, len(toBeMergedRequest.ExposedPorts))
assert.Equal(t, "67890/tcp", toBeMergedRequest.ExposedPorts[0])

// req should be merged with toBeMergedRequest
assert.Equal(t, "FOO", req.Env["FOO"])
assert.Equal(t, "BAR", req.Env["BAR"])
assert.Equal(t, "bar", req.Image)
assert.Equal(t, []string{"12345/tcp", "67890/tcp"}, req.ExposedPorts)
assert.Equal(t, []string{"foo", "bar", "baaz", "foo1", "bar1"}, req.Networks)
assert.Equal(t, []string{"foo0", "foo1", "foo2", "foo3"}, req.NetworkAliases["foo"])
assert.Equal(t, []string{"bar"}, req.NetworkAliases["foo1"])
assert.Equal(t, wait.ForLog("foo"), req.WaitingFor)
}

func TestParseDockerIgnore(t *testing.T) {
testCases := []struct {
filePath string
Expand Down
148 changes: 0 additions & 148 deletions generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@ import (
"context"
"errors"
"fmt"
"strings"
"sync"
"time"

"dario.cat/mergo"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"

"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
"github.com/testcontainers/testcontainers-go/internal/testcontainerssession"
"github.com/testcontainers/testcontainers-go/wait"
)

var (
Expand All @@ -31,147 +24,6 @@ type GenericContainerRequest struct {
Reuse bool // reuse an existing container if it exists or create a new one. a container name mustn't be empty
}

// ContainerCustomizer is an interface that can be used to configure the Testcontainers container
// request. The passed request will be merged with the default one.
type ContainerCustomizer interface {
Customize(req *GenericContainerRequest)
}

// CustomizeRequestOption is a type that can be used to configure the Testcontainers container request.
// The passed request will be merged with the default one.
type CustomizeRequestOption func(req *GenericContainerRequest)

func (opt CustomizeRequestOption) Customize(req *GenericContainerRequest) {
opt(req)
}

// CustomizeRequest returns a function that can be used to merge the passed container request with the one that is used by the container.
// Slices and Maps will be appended.
func CustomizeRequest(src GenericContainerRequest) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
if err := mergo.Merge(req, &src, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
fmt.Printf("error merging container request, keeping the original one. Error: %v", err)
return
}
}
}

// WithImage sets the image for a container
func WithImage(image string) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.Image = image
}
}

// imageSubstitutor {
// ImageSubstitutor represents a way to substitute container image names
type ImageSubstitutor interface {
// Description returns the name of the type and a short description of how it modifies the image.
// Useful to be printed in logs
Description() string
Substitute(image string) (string, error)
}

// }

// WithImageSubstitutors sets the image substitutors for a container
func WithImageSubstitutors(fn ...ImageSubstitutor) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.ImageSubstitutors = fn
}
}

// WithConfigModifier allows to override the default container config
func WithConfigModifier(modifier func(config *container.Config)) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.ConfigModifier = modifier
}
}

// WithEndpointSettingsModifier allows to override the default endpoint settings
func WithEndpointSettingsModifier(modifier func(settings map[string]*network.EndpointSettings)) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.EnpointSettingsModifier = modifier
}
}

// WithHostConfigModifier allows to override the default host config
func WithHostConfigModifier(modifier func(hostConfig *container.HostConfig)) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.HostConfigModifier = modifier
}
}

// WithNetwork creates a network with the given name and attaches the container to it, setting the network alias
// on that network to the given alias.
// If the network already exists, checking if the network name already exists, it will be reused.
func WithNetwork(networkName string, alias string) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
_, err := GenericNetwork(context.Background(), GenericNetworkRequest{
NetworkRequest: NetworkRequest{
Name: networkName,
CheckDuplicate: true, // force the Docker provider to reuse an existing network
},
})
if err != nil && !strings.Contains(err.Error(), "already exists") {
logger := req.Logger
if logger == nil {
logger = Logger
}
logger.Printf("Failed to create network '%s'. Container won't be attached to this network: %v", networkName, err)
return
}

// attaching to the network because it was created with success or it already existed.
req.Networks = append(req.Networks, networkName)

if req.NetworkAliases == nil {
req.NetworkAliases = make(map[string][]string)
}
req.NetworkAliases[networkName] = []string{alias}
}
}

// Executable represents an executable command to be sent to a container
// as part of the PostStart lifecycle hook.
type Executable interface {
AsCommand() []string
}

// WithStartupCommand will execute the command representation of each Executable into the container.
// It will leverage the container lifecycle hooks to call the command right after the container
// is started.
func WithStartupCommand(execs ...Executable) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
startupCommandsHook := ContainerLifecycleHooks{
PostStarts: []ContainerHook{},
}

for _, exec := range execs {
execFn := func(ctx context.Context, c Container) error {
_, _, err := c.Exec(ctx, exec.AsCommand())
return err
}

startupCommandsHook.PostStarts = append(startupCommandsHook.PostStarts, execFn)
}

req.LifecycleHooks = append(req.LifecycleHooks, startupCommandsHook)
}
}

// WithWaitStrategy sets the wait strategy for a container, using 60 seconds as deadline
func WithWaitStrategy(strategies ...wait.Strategy) CustomizeRequestOption {
return WithWaitStrategyAndDeadline(60*time.Second, strategies...)
}

// WithWaitStrategyAndDeadline sets the wait strategy for a container, including deadline
func WithWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Strategy) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.WaitingFor = wait.ForAll(strategies...).WithDeadline(deadline)
}
}

// GenericNetworkRequest represents parameters to a generic network
type GenericNetworkRequest struct {
NetworkRequest // embedded request for provider
Expand Down
41 changes: 0 additions & 41 deletions generic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ package testcontainers
import (
"context"
"errors"
"io"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/testcontainers/testcontainers-go/exec"
"github.com/testcontainers/testcontainers-go/wait"
)

Expand Down Expand Up @@ -99,41 +96,3 @@ func TestGenericReusableContainer(t *testing.T) {
})
}
}

type testExecutable struct {
cmds []string
}

func (t testExecutable) AsCommand() []string {
return t.cmds
}

func TestWithStartupCommand(t *testing.T) {
req := GenericContainerRequest{
ContainerRequest: ContainerRequest{
Image: "alpine",
Entrypoint: []string{"tail", "-f", "/dev/null"},
},
Started: true,
}

testExec := testExecutable{
cmds: []string{"touch", "/tmp/.testcontainers"},
}

WithStartupCommand(testExec)(&req)

c, err := GenericContainer(context.Background(), req)
require.NoError(t, err)
defer func() {
err = c.Terminate(context.Background())
require.NoError(t, err)
}()

_, reader, err := c.Exec(context.Background(), []string{"ls", "/tmp/.testcontainers"}, exec.Multiplexed())
require.NoError(t, err)

content, err := io.ReadAll(reader)
require.NoError(t, err)
assert.Equal(t, "/tmp/.testcontainers\n", string(content))
}
Loading