diff --git a/docker.go b/docker.go
index 34f62dfe1c..53b3de7c8b 100644
--- a/docker.go
+++ b/docker.go
@@ -1246,6 +1246,7 @@ func daemonHost(ctx context.Context, p *DockerProvider) (string, error) {
return p.hostCache, nil
}
+// Deprecated: use network.New instead
// CreateNetwork returns the object representing a new network identified by its name
func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest) (Network, error) {
var err error
diff --git a/docker_test.go b/docker_test.go
index ee1bd92216..80fb02e3df 100644
--- a/docker_test.go
+++ b/docker_test.go
@@ -23,7 +23,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
"github.com/testcontainers/testcontainers-go/wait"
)
@@ -45,86 +44,6 @@ func init() {
}
}
-// testNetworkAliases {
-func TestContainerAttachedToNewNetwork(t *testing.T) {
- aliases := []string{"alias1", "alias2", "alias3"}
- networkName := "new-network"
- ctx := context.Background()
- gcr := GenericContainerRequest{
- ProviderType: providerType,
- ContainerRequest: ContainerRequest{
- Image: nginxAlpineImage,
- ExposedPorts: []string{
- nginxDefaultPort,
- },
- Networks: []string{
- networkName,
- },
- NetworkAliases: map[string][]string{
- networkName: aliases,
- },
- },
- Started: true,
- }
-
- newNetwork, err := GenericNetwork(ctx, GenericNetworkRequest{
- ProviderType: providerType,
- NetworkRequest: NetworkRequest{
- Name: networkName,
- CheckDuplicate: true,
- },
- })
- if err != nil {
- t.Fatal(err)
- }
- t.Cleanup(func() {
- require.NoError(t, newNetwork.Remove(ctx))
- })
-
- nginx, err := GenericContainer(ctx, gcr)
-
- require.NoError(t, err)
- terminateContainerOnEnd(t, ctx, nginx)
-
- networks, err := nginx.Networks(ctx)
- if err != nil {
- t.Fatal(err)
- }
- if len(networks) != 1 {
- t.Errorf("Expected networks 1. Got '%d'.", len(networks))
- }
- network := networks[0]
- if network != networkName {
- t.Errorf("Expected network name '%s'. Got '%s'.", networkName, network)
- }
-
- networkAliases, err := nginx.NetworkAliases(ctx)
- if err != nil {
- t.Fatal(err)
- }
- if len(networkAliases) != 1 {
- t.Errorf("Expected network aliases for 1 network. Got '%d'.", len(networkAliases))
- }
-
- networkAlias := networkAliases[networkName]
-
- require.NotEmpty(t, networkAlias)
-
- for _, alias := range aliases {
- require.Contains(t, networkAlias, alias)
- }
-
- networkIP, err := nginx.ContainerIP(ctx)
- if err != nil {
- t.Fatal(err)
- }
- if len(networkIP) == 0 {
- t.Errorf("Expected an IP address, got %v", networkIP)
- }
-}
-
-// }
-
func TestContainerWithHostNetworkOptions(t *testing.T) {
if os.Getenv("XDG_RUNTIME_DIR") != "" {
t.Skip("Skipping test that requires host network access when running in a container")
@@ -594,54 +513,6 @@ func TestContainerCreation(t *testing.T) {
}
}
-func TestContainerIPs(t *testing.T) {
- ctx := context.Background()
-
- networkName := "new-network"
- newNetwork, err := GenericNetwork(ctx, GenericNetworkRequest{
- ProviderType: providerType,
- NetworkRequest: NetworkRequest{
- Name: networkName,
- CheckDuplicate: true,
- },
- })
- if err != nil {
- t.Fatal(err)
- }
-
- t.Cleanup(func() {
- require.NoError(t, newNetwork.Remove(ctx))
- })
-
- nginxC, err := GenericContainer(ctx, GenericContainerRequest{
- ProviderType: providerType,
- ContainerRequest: ContainerRequest{
- Image: nginxAlpineImage,
- ExposedPorts: []string{
- nginxDefaultPort,
- },
- Networks: []string{
- "bridge",
- networkName,
- },
- WaitingFor: wait.ForListeningPort(nginxDefaultPort),
- },
- Started: true,
- })
-
- require.NoError(t, err)
- terminateContainerOnEnd(t, ctx, nginxC)
-
- ips, err := nginxC.ContainerIPs(ctx)
- if err != nil {
- t.Fatal(err)
- }
-
- if len(ips) != 2 {
- t.Errorf("Expected two IP addresses, got %v", len(ips))
- }
-}
-
func TestContainerCreationWithName(t *testing.T) {
ctx := context.Background()
@@ -1298,7 +1169,8 @@ func TestContainerWithTmpFs(t *testing.T) {
path := "/testtmpfs/test.file"
- c, _, err := container.Exec(ctx, []string{"ls", path})
+ // exec_reader_example {
+ c, reader, err := container.Exec(ctx, []string{"ls", path})
if err != nil {
t.Fatal(err)
}
@@ -1306,6 +1178,17 @@ func TestContainerWithTmpFs(t *testing.T) {
t.Fatalf("File %s should not have existed, expected return code 1, got %v", path, c)
}
+ buf := new(strings.Builder)
+ _, err = io.Copy(buf, reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // See the logs from the command execution.
+ t.Log(buf.String())
+ // }
+
+ // exec_example {
c, _, err = container.Exec(ctx, []string{"touch", path})
if err != nil {
t.Fatal(err)
@@ -1313,6 +1196,7 @@ func TestContainerWithTmpFs(t *testing.T) {
if c != 0 {
t.Fatalf("File %s should have been created successfully, expected return code 0, got %v", path, c)
}
+ // }
c, _, err = container.Exec(ctx, []string{"ls", path})
if err != nil {
@@ -1812,66 +1696,6 @@ func TestDockerContainerResources(t *testing.T) {
assert.Equal(t, expected, resp.HostConfig.Ulimits)
}
-func TestContainerWithReaperNetwork(t *testing.T) {
- if testcontainersdocker.IsWindows() {
- t.Skip("Skip for Windows. See https://stackoverflow.com/questions/43784916/docker-for-windows-networking-container-with-multiple-network-interfaces")
- }
-
- ctx := context.Background()
- networks := []string{
- "test_network_" + randomString(),
- "test_network_" + randomString(),
- }
-
- for _, nw := range networks {
- nr := NetworkRequest{
- Name: nw,
- Attachable: true,
- }
- n, err := GenericNetwork(ctx, GenericNetworkRequest{
- ProviderType: providerType,
- NetworkRequest: nr,
- })
- assert.Nil(t, err)
- // use t.Cleanup to run after terminateContainerOnEnd
- t.Cleanup(func() {
- err := n.Remove(ctx)
- assert.NoError(t, err)
- })
- }
-
- req := ContainerRequest{
- Image: nginxAlpineImage,
- ExposedPorts: []string{nginxDefaultPort},
- WaitingFor: wait.ForAll(
- wait.ForListeningPort(nginxDefaultPort),
- wait.ForLog("Configuration complete; ready for start up"),
- ),
- Networks: networks,
- }
-
- nginxC, err := GenericContainer(ctx, GenericContainerRequest{
- ProviderType: providerType,
- ContainerRequest: req,
- Started: true,
- })
-
- require.NoError(t, err)
- terminateContainerOnEnd(t, ctx, nginxC)
-
- containerId := nginxC.GetContainerID()
-
- cli, err := NewDockerClientWithOpts(ctx)
- assert.Nil(t, err)
- defer cli.Close()
-
- cnt, err := cli.ContainerInspect(ctx, containerId)
- assert.Nil(t, err)
- assert.Equal(t, 2, len(cnt.NetworkSettings.Networks))
- assert.NotNil(t, cnt.NetworkSettings.Networks[networks[0]])
- assert.NotNil(t, cnt.NetworkSettings.Networks[networks[1]])
-}
-
func TestContainerCapAdd(t *testing.T) {
if providerType == ProviderPodman {
t.Skip("Rootless Podman does not support setting cap-add/cap-drop")
@@ -2105,19 +1929,6 @@ func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr Container)
})
}
-func randomString() string {
- rand.New(rand.NewSource(time.Now().UnixNano()))
- chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
- "abcdefghijklmnopqrstuvwxyz" +
- "0123456789")
- length := 8
- var b strings.Builder
- for i := 0; i < length; i++ {
- b.WriteRune(chars[rand.Intn(len(chars))])
- }
- return b.String()
-}
-
func TestDockerProviderFindContainerByName(t *testing.T) {
ctx := context.Background()
provider, err := NewDockerProvider(WithLogger(TestLogger(t)))
diff --git a/docs/features/common_functional_options.md b/docs/features/common_functional_options.md
index 0b53f4c2b6..c2c51739a0 100644
--- a/docs/features/common_functional_options.md
+++ b/docs/features/common_functional_options.md
@@ -57,9 +57,20 @@ You could use this feature to run a custom script, or to run a command that is n
- Not available until the next release of testcontainers-go :material-tag: main
-By default, the container is started in the default Docker network. If you want to use a different Docker network, you can use the `WithNetwork(networkName string, alias string)` option, which receives the new network name and an alias as parameters, creating the new network, attaching the container to it, and setting the network alias for that network.
+By default, the container is started in the default Docker network. If you want to use an already existing Docker network you created in your code, you can use the `network.WithNetwork(aliases []string, nw *testcontainers.DockerNetwork)` option, which receives an alias as parameter and your network, attaching the container to it, and setting the network alias for that network.
-If the network already exists, _Testcontainers for Go_ won't create a new one, but it will attach the container to it and set the network alias.
+In the case you need to retrieve the network name, you can simply read it from the struct's `Name` field. E.g. `nw.Name`.
+
+!!!warning
+ This option is not checking whether the network exists or not. If you use a network that doesn't exist, the container will start in the default Docker network, as in the default behavior.
+
+#### WithNewNetwork
+
+- Not available until the next release of testcontainers-go :material-tag: main
+
+If you want to attach your containers to a throw-away network, you can use the `network.WithNewNetwork(ctx context.Context, aliases []string, opts ...network.NetworkCustomizer)` option, which receives an alias as parameter, creating the new network with a random name, attaching the container to it, and setting the network alias for that network.
+
+In the case you need to retrieve the network name, you can use the `Networks(ctx)` method of the `Container` interface, right after it's running, which returns a slice of strings with the names of the networks where the container is attached.
#### Docker type modifiers
diff --git a/docs/features/creating_networks.md b/docs/features/creating_networks.md
index 0ecde6be13..d2889d0b3b 100644
--- a/docs/features/creating_networks.md
+++ b/docs/features/creating_networks.md
@@ -1,13 +1,30 @@
# How to create a network
-Apart from creating containers, `Testcontainers for Go` also allows you to create networks. This is useful when you need to connect multiple containers to the same network.
+Apart from creating containers, `Testcontainers for Go` allows you to create networks. This is useful when you need to connect multiple containers to the same network.
-## Usage example
+- Not available until the next release of testcontainers-go :material-tag: main
-
-[Creating a network](../../network_test.go) inside_block:createNetwork
-
+For that, please import the `testcontainers/network` package.
+
+```go
+import "github.com/testcontainers/testcontainers-go/network"
+```
+
+Then, you can create a network using the `network.New` function. This function receives a variadic list of options that can be used to configure the network.
+
+- `WithAttachable()`
+- `WithCheckDuplicate()`
+- `WithDriver(driver string)`
+- `WithEnableIPv6()`
+- `WithInternal()`
+- `WithLabels(labels map[string]string)`
+- `WithIPAMConfig(config *network.IPAMConfig)`
+
+It's important to mention that the name of the network is automatically generated by the library, and it's not possible to set it manually. However, you can retrieve the name of the network using the `Name` field of the `DockerNetwork` struct returned by the `New` function.
+
+## Usage example
-[Creating a network with IPAM](../../network_test.go) inside_block:withIPAM
-
\ No newline at end of file
+[Creating a network](../../network/network_test.go) inside_block:createNetwork
+[Creating a network with options](../../network/network_test.go) inside_block:newNetworkWithOptions
+
\ No newline at end of file
diff --git a/docs/features/networking.md b/docs/features/networking.md
index b852e552b3..9eb86e8da2 100644
--- a/docs/features/networking.md
+++ b/docs/features/networking.md
@@ -72,6 +72,8 @@ Docker provides the ability for you to create custom networks and place containe
!!! tip
Note that _Testcontainers for Go_ allows a container to be on multiple networks including network aliases.
+For more information about how to create networks using _Testcontainers for Go_, please refer to the [How to create a network](./creating_networks.md) section.
+
-[Creating custom networks](../../docker_test.go) inside_block:testNetworkAliases
+[Creating custom networks](../../network/network_test.go) inside_block:testNetworkAliases
diff --git a/docs/features/override_container_command.md b/docs/features/override_container_command.md
index 1a9b65bb7f..c7a369ed6f 100644
--- a/docs/features/override_container_command.md
+++ b/docs/features/override_container_command.md
@@ -1,7 +1,9 @@
-# Sending a CMD to a Container
+# Executing commands
-If you would like to send a CMD (command) to a container, you can pass it in to
-the container request via the `Cmd` field...
+## Container startup command
+
+By default the container will execute whatever command is specified in the image's Dockerfile. If you would like to send a CMD (command) to a container, you can pass it in to
+the container request via the `Cmd` field. For example:
```go
req := ContainerRequest{
@@ -13,3 +15,19 @@ req := ContainerRequest{
}
```
+## Executing a command
+
+You can execute a command inside a running container, similar to a `docker exec` call:
+
+
+[Executing a command](../../docker_test.go) inside_block:exec_example
+
+
+This can be useful for software that has a command line administration tool. You can also get the logs of the command execution (from an object that implements the [io.Reader](https://pkg.go.dev/io#Reader) interface). For example:
+
+
+
+[Command logs](../../docker_test.go) inside_block:exec_reader_example
+
+
+This is done this way, because it brings more flexibility to the user, rather than returning a string.
diff --git a/examples/toxiproxy/go.mod b/examples/toxiproxy/go.mod
index eab9c80901..95d36aee32 100644
--- a/examples/toxiproxy/go.mod
+++ b/examples/toxiproxy/go.mod
@@ -4,6 +4,7 @@ go 1.20
require (
github.com/Shopify/toxiproxy/v2 v2.7.0
+ github.com/docker/docker v24.0.7+incompatible
github.com/go-redis/redis/v8 v8.11.5
github.com/google/uuid v1.4.0
github.com/testcontainers/testcontainers-go v0.26.0
@@ -21,7 +22,6 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
- github.com/docker/docker v24.0.7+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
diff --git a/examples/toxiproxy/toxiproxy_test.go b/examples/toxiproxy/toxiproxy_test.go
index f1ba128b59..5ec70ef899 100644
--- a/examples/toxiproxy/toxiproxy_test.go
+++ b/examples/toxiproxy/toxiproxy_test.go
@@ -10,29 +10,25 @@ import (
"github.com/go-redis/redis/v8"
"github.com/google/uuid"
- "github.com/testcontainers/testcontainers-go"
+ "github.com/testcontainers/testcontainers-go/network"
)
func TestToxiproxy(t *testing.T) {
ctx := context.Background()
- newNetwork, err := testcontainers.GenericNetwork(ctx, testcontainers.GenericNetworkRequest{
- ProviderType: testcontainers.ProviderDocker,
- NetworkRequest: testcontainers.NetworkRequest{
- Name: "newNetwork",
- CheckDuplicate: true,
- },
- })
+ newNetwork, err := network.New(ctx, network.WithCheckDuplicate())
if err != nil {
t.Fatal(err)
}
- toxiproxyContainer, err := startContainer(ctx, "newNetwork", []string{"toxiproxy"})
+ networkName := newNetwork.Name
+
+ toxiproxyContainer, err := startContainer(ctx, networkName, []string{"toxiproxy"})
if err != nil {
t.Fatal(err)
}
- redisContainer, err := setupRedis(ctx, "newNetwork", []string{"redis"})
+ redisContainer, err := setupRedis(ctx, networkName, []string{"redis"})
if err != nil {
t.Fatal(err)
}
@@ -73,7 +69,12 @@ func TestToxiproxy(t *testing.T) {
t.Fatal(err)
}
redisClient := redis.NewClient(options)
- defer flushRedis(ctx, *redisClient)
+ defer func() {
+ err := flushRedis(ctx, *redisClient)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }()
// Set data
key := fmt.Sprintf("{user.%s}.favoritefood", uuid.NewString())
diff --git a/generic.go b/generic.go
index 01a4267f17..837d0191f7 100644
--- a/generic.go
+++ b/generic.go
@@ -24,12 +24,14 @@ type GenericContainerRequest struct {
Reuse bool // reuse an existing container if it exists or create a new one. a container name mustn't be empty
}
+// Deprecated: will be removed in the future.
// GenericNetworkRequest represents parameters to a generic network
type GenericNetworkRequest struct {
NetworkRequest // embedded request for provider
ProviderType ProviderType // which provider to use, Docker if empty
}
+// Deprecated: use network.New instead
// GenericNetwork creates a generic network with parameters
func GenericNetwork(ctx context.Context, req GenericNetworkRequest) (Network, error) {
provider, err := req.ProviderType.GetProvider()
diff --git a/modules/localstack/examples_test.go b/modules/localstack/examples_test.go
index 277e297405..ad0c3b28e8 100644
--- a/modules/localstack/examples_test.go
+++ b/modules/localstack/examples_test.go
@@ -14,6 +14,7 @@ import (
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/exec"
"github.com/testcontainers/testcontainers-go/modules/localstack"
+ "github.com/testcontainers/testcontainers-go/network"
"github.com/testcontainers/testcontainers-go/wait"
)
@@ -51,17 +52,13 @@ func ExampleRunContainer_withNetwork() {
// localstackWithNetwork {
ctx := context.Background()
- nwName := "localstack-network"
-
- _, err := testcontainers.GenericNetwork(ctx, testcontainers.GenericNetworkRequest{
- NetworkRequest: testcontainers.NetworkRequest{
- Name: nwName,
- },
- })
+ newNetwork, err := network.New(ctx, network.WithCheckDuplicate())
if err != nil {
panic(err)
}
+ nwName := newNetwork.Name
+
localstackContainer, err := localstack.RunContainer(
ctx,
testcontainers.CustomizeRequest(testcontainers.GenericContainerRequest{
@@ -91,11 +88,9 @@ func ExampleRunContainer_withNetwork() {
}
fmt.Println(len(networks))
- fmt.Println(networks[0])
// Output:
// 1
- // localstack-network
}
func ExampleRunContainer_legacyMode() {
diff --git a/modules/localstack/localstack.go b/modules/localstack/localstack.go
index 4fb544a462..8fdc1873dd 100644
--- a/modules/localstack/localstack.go
+++ b/modules/localstack/localstack.go
@@ -11,6 +11,7 @@ import (
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
+ "github.com/testcontainers/testcontainers-go/network"
"github.com/testcontainers/testcontainers-go/wait"
)
@@ -61,9 +62,9 @@ func isVersion2(image string) bool {
// 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.
-// Deprecated: use testcontainers.WithNetwork instead
+// Deprecated: use network.WithNetwork or network.WithNewNetwork instead
func WithNetwork(networkName string, alias string) testcontainers.CustomizeRequestOption {
- return testcontainers.WithNetwork(networkName, alias)
+ return network.WithNewNetwork(context.Background(), []string{alias}, network.WithCheckDuplicate())
}
// RunContainer creates an instance of the LocalStack container type, being possible to pass a custom request and options:
diff --git a/modules/localstack/localstack_test.go b/modules/localstack/localstack_test.go
index e92152b85d..4d65018722 100644
--- a/modules/localstack/localstack_test.go
+++ b/modules/localstack/localstack_test.go
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
+ "github.com/testcontainers/testcontainers-go/network"
"github.com/testcontainers/testcontainers-go/wait"
)
@@ -154,11 +155,12 @@ func TestStartV2WithNetwork(t *testing.T) {
ctx := context.Background()
// withCustomContainerRequest {
- networkName := "localstack-network-v2"
+ nw, err := network.New(ctx)
+ require.Nil(t, err)
localstack, err := RunContainer(
ctx,
- WithNetwork(networkName, "localstack"),
+ network.WithNetwork([]string{"localstack"}, nw),
testcontainers.CustomizeRequest(testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "localstack/localstack:2.0.0",
@@ -170,6 +172,8 @@ func TestStartV2WithNetwork(t *testing.T) {
require.Nil(t, err)
assert.NotNil(t, localstack)
+ networkName := nw.Name
+
cli, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "amazon/aws-cli:2.7.27",
diff --git a/modules/pulsar/pulsar_test.go b/modules/pulsar/pulsar_test.go
index 026b59b2c0..bc7975a82c 100644
--- a/modules/pulsar/pulsar_test.go
+++ b/modules/pulsar/pulsar_test.go
@@ -18,6 +18,7 @@ import (
"github.com/testcontainers/testcontainers-go"
testcontainerspulsar "github.com/testcontainers/testcontainers-go/modules/pulsar"
+ tcnetwork "github.com/testcontainers/testcontainers-go/network"
)
// logConsumerForTesting {
@@ -35,14 +36,11 @@ func TestPulsar(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
- nwName := "pulsar-test"
- nw, err := testcontainers.GenericNetwork(context.Background(), testcontainers.GenericNetworkRequest{
- NetworkRequest: testcontainers.NetworkRequest{
- Name: nwName,
- },
- })
+ nw, err := tcnetwork.New(ctx, tcnetwork.WithCheckDuplicate())
require.NoError(t, err)
+ nwName := nw.Name
+
tests := []struct {
name string
opts []testcontainers.ContainerCustomizer
diff --git a/network.go b/network.go
index 82b4d19605..7ff0bf5ab3 100644
--- a/network.go
+++ b/network.go
@@ -13,21 +13,26 @@ type NetworkProvider interface {
GetNetwork(context.Context, NetworkRequest) (types.NetworkResource, error) // get a network
}
+// Deprecated: will be removed in the future
// Network allows getting info about a single network instance
type Network interface {
Remove(context.Context) error // removes the network
}
+// Deprecated: will be removed in the future.
type DefaultNetwork string
+// Deprecated: will be removed in the future.
func (n DefaultNetwork) ApplyGenericTo(opts *GenericProviderOptions) {
opts.DefaultNetwork = string(n)
}
+// Deprecated: will be removed in the future.
func (n DefaultNetwork) ApplyDockerTo(opts *DockerProviderOptions) {
opts.DefaultNetwork = string(n)
}
+// Deprecated: will be removed in the future
// NetworkRequest represents the parameters used to get a network
type NetworkRequest struct {
Driver string
diff --git a/network/network.go b/network/network.go
new file mode 100644
index 0000000000..49bbabad99
--- /dev/null
+++ b/network/network.go
@@ -0,0 +1,162 @@
+package network
+
+import (
+ "context"
+
+ "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/network"
+ "github.com/google/uuid"
+
+ "github.com/testcontainers/testcontainers-go"
+)
+
+// New creates a new network with a random UUID name, calling the already existing GenericNetwork APIs.
+// Those existing APIs are deprecated and will be removed in the future, so this function will
+// implement the new network APIs when they will be available.
+// By default, the network is created with the following options:
+// - Driver: bridge
+// - Labels: the Testcontainers for Go generic labels, to be managed by Ryuk. Please see the GenericLabels() function
+// And those options can be modified by the user, using the CreateModifier function field.
+func New(ctx context.Context, opts ...NetworkCustomizer) (*testcontainers.DockerNetwork, error) {
+ nc := types.NetworkCreate{
+ Driver: "bridge",
+ Labels: testcontainers.GenericLabels(),
+ }
+
+ for _, opt := range opts {
+ opt.Customize(&nc)
+ }
+
+ //nolint:staticcheck
+ netReq := testcontainers.NetworkRequest{
+ Driver: nc.Driver,
+ CheckDuplicate: nc.CheckDuplicate,
+ Internal: nc.Internal,
+ EnableIPv6: nc.EnableIPv6,
+ Name: uuid.NewString(),
+ Labels: nc.Labels,
+ Attachable: nc.Attachable,
+ IPAM: nc.IPAM,
+ }
+
+ //nolint:staticcheck
+ n, err := testcontainers.GenericNetwork(ctx, testcontainers.GenericNetworkRequest{
+ NetworkRequest: netReq,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Return a DockerNetwork struct instead of the Network interface,
+ // following the "accept interface, return struct" pattern.
+ return n.(*testcontainers.DockerNetwork), nil
+}
+
+// NetworkCustomizer is an interface that can be used to configure the network create request.
+type NetworkCustomizer interface {
+ Customize(req *types.NetworkCreate)
+}
+
+// CustomizeNetworkOption is a type that can be used to configure the network create request.
+type CustomizeNetworkOption func(req *types.NetworkCreate)
+
+// Customize implements the NetworkCustomizer interface,
+// applying the option to the network create request.
+func (opt CustomizeNetworkOption) Customize(req *types.NetworkCreate) {
+ opt(req)
+}
+
+// WithAttachable allows to set the network as attachable.
+func WithAttachable() CustomizeNetworkOption {
+ return func(original *types.NetworkCreate) {
+ original.Attachable = true
+ }
+}
+
+// WithCheckDuplicate allows to check if a network with the same name already exists.
+func WithCheckDuplicate() CustomizeNetworkOption {
+ return func(original *types.NetworkCreate) {
+ original.CheckDuplicate = true
+ }
+}
+
+// WithDriver allows to override the default network driver, which is "bridge".
+func WithDriver(driver string) CustomizeNetworkOption {
+ return func(original *types.NetworkCreate) {
+ original.Driver = driver
+ }
+}
+
+// WithEnableIPv6 allows to set the network as IPv6 enabled.
+// Please use this option if and only if IPv6 is enabled on the Docker daemon.
+func WithEnableIPv6() CustomizeNetworkOption {
+ return func(original *types.NetworkCreate) {
+ original.EnableIPv6 = true
+ }
+}
+
+// WithInternal allows to set the network as internal.
+func WithInternal() CustomizeNetworkOption {
+ return func(original *types.NetworkCreate) {
+ original.Internal = true
+ }
+}
+
+// WithLabels allows to set the network labels, adding the new ones
+// to the default Testcontainers for Go labels.
+func WithLabels(labels map[string]string) CustomizeNetworkOption {
+ return func(original *types.NetworkCreate) {
+ for k, v := range labels {
+ original.Labels[k] = v
+ }
+ }
+}
+
+// WithIPAM allows to change the default IPAM configuration.
+func WithIPAM(ipam *network.IPAM) CustomizeNetworkOption {
+ return func(original *types.NetworkCreate) {
+ original.IPAM = ipam
+ }
+}
+
+// WithNetwork reuses an already existing network, attaching the container to it.
+// Finally it sets the network alias on that network to the given alias.
+func WithNetwork(aliases []string, nw *testcontainers.DockerNetwork) testcontainers.CustomizeRequestOption {
+ return func(req *testcontainers.GenericContainerRequest) {
+ networkName := nw.Name
+
+ // 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] = aliases
+ }
+}
+
+// WithNewNetwork creates a new network with random name and customizers, and attaches the container to it.
+// Finally it sets the network alias on that network to the given alias.
+func WithNewNetwork(ctx context.Context, aliases []string, opts ...NetworkCustomizer) testcontainers.CustomizeRequestOption {
+ return func(req *testcontainers.GenericContainerRequest) {
+ newNetwork, err := New(ctx, opts...)
+ if err != nil {
+ logger := req.Logger
+ if logger == nil {
+ logger = testcontainers.Logger
+ }
+ logger.Printf("failed to create network. Container won't be attached to it: %v", err)
+ return
+ }
+
+ networkName := newNetwork.Name
+
+ // 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] = aliases
+ }
+}
diff --git a/network/network_test.go b/network/network_test.go
new file mode 100644
index 0000000000..4f1819b35b
--- /dev/null
+++ b/network/network_test.go
@@ -0,0 +1,559 @@
+package network_test
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "testing"
+ "time"
+
+ "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/filters"
+ dockernetwork "github.com/docker/docker/api/types/network"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/testcontainers/testcontainers-go"
+ "github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
+ "github.com/testcontainers/testcontainers-go/network"
+ "github.com/testcontainers/testcontainers-go/wait"
+)
+
+const (
+ nginxAlpineImage = "docker.io/nginx:alpine"
+ nginxDefaultPort = "80/tcp"
+)
+
+// Create a network.
+func ExampleNew() {
+ // createNetwork {
+ ctx := context.Background()
+
+ net, err := network.New(ctx,
+ network.WithCheckDuplicate(),
+ network.WithAttachable(),
+ network.WithInternal(),
+ network.WithLabels(map[string]string{"this-is-a-test": "value"}),
+ )
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer func() {
+ if err := net.Remove(ctx); err != nil {
+ panic(err)
+ }
+ }()
+
+ networkName := net.Name
+ // }
+
+ nginxC, _ := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{
+ Image: "nginx:alpine",
+ ExposedPorts: []string{
+ "80/tcp",
+ },
+ Networks: []string{
+ networkName,
+ },
+ },
+ Started: true,
+ })
+ defer func() {
+ if err := nginxC.Terminate(ctx); err != nil {
+ log.Fatalf("failed to terminate container: %s", err)
+ }
+ }()
+
+ client, err := testcontainers.NewDockerClientWithOpts(context.Background())
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ args := filters.NewArgs()
+ args.Add("name", networkName)
+
+ resources, err := client.NetworkList(context.Background(), types.NetworkListOptions{
+ Filters: args,
+ })
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ fmt.Println(len(resources))
+
+ newNetwork := resources[0]
+
+ expectedLabels := testcontainers.GenericLabels()
+ expectedLabels["this-is-a-test"] = "true"
+
+ fmt.Println(newNetwork.Attachable)
+ fmt.Println(newNetwork.Internal)
+ fmt.Println(newNetwork.Labels["this-is-a-test"])
+
+ state, err := nginxC.State(ctx)
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ fmt.Println(state.Running)
+
+ // Output:
+ // 1
+ // true
+ // true
+ // value
+ // true
+}
+
+// testNetworkAliases {
+func TestContainerAttachedToNewNetwork(t *testing.T) {
+ ctx := context.Background()
+
+ newNetwork, err := network.New(ctx, network.WithCheckDuplicate())
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() {
+ require.NoError(t, newNetwork.Remove(ctx))
+ })
+
+ networkName := newNetwork.Name
+
+ aliases := []string{"alias1", "alias2", "alias3"}
+
+ gcr := testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{
+ Image: nginxAlpineImage,
+ ExposedPorts: []string{
+ nginxDefaultPort,
+ },
+ Networks: []string{
+ networkName,
+ },
+ NetworkAliases: map[string][]string{
+ networkName: aliases,
+ },
+ },
+ Started: true,
+ }
+
+ nginx, err := testcontainers.GenericContainer(ctx, gcr)
+ require.NoError(t, err)
+ defer func() {
+ require.NoError(t, nginx.Terminate(ctx))
+ }()
+
+ networks, err := nginx.Networks(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(networks) != 1 {
+ t.Errorf("Expected networks 1. Got '%d'.", len(networks))
+ }
+ network := networks[0]
+ if network != networkName {
+ t.Errorf("Expected network name '%s'. Got '%s'.", networkName, network)
+ }
+
+ networkAliases, err := nginx.NetworkAliases(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(networkAliases) != 1 {
+ t.Errorf("Expected network aliases for 1 network. Got '%d'.", len(networkAliases))
+ }
+
+ networkAlias := networkAliases[networkName]
+
+ require.NotEmpty(t, networkAlias)
+
+ for _, alias := range aliases {
+ require.Contains(t, networkAlias, alias)
+ }
+
+ networkIP, err := nginx.ContainerIP(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(networkIP) == 0 {
+ t.Errorf("Expected an IP address, got %v", networkIP)
+ }
+}
+
+// }
+
+func TestContainerIPs(t *testing.T) {
+ ctx := context.Background()
+
+ newNetwork, err := network.New(ctx, network.WithCheckDuplicate())
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() {
+ require.NoError(t, newNetwork.Remove(ctx))
+ })
+
+ networkName := newNetwork.Name
+
+ nginx, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{
+ Image: nginxAlpineImage,
+ ExposedPorts: []string{
+ nginxDefaultPort,
+ },
+ Networks: []string{
+ "bridge",
+ networkName,
+ },
+ WaitingFor: wait.ForListeningPort(nginxDefaultPort),
+ },
+ Started: true,
+ })
+ require.NoError(t, err)
+ defer func() {
+ require.NoError(t, nginx.Terminate(ctx))
+ }()
+
+ ips, err := nginx.ContainerIPs(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(ips) != 2 {
+ t.Errorf("Expected two IP addresses, got %v", len(ips))
+ }
+}
+
+func TestContainerWithReaperNetwork(t *testing.T) {
+ if testcontainersdocker.IsWindows() {
+ t.Skip("Skip for Windows. See https://stackoverflow.com/questions/43784916/docker-for-windows-networking-container-with-multiple-network-interfaces")
+ }
+
+ ctx := context.Background()
+ networks := []string{}
+
+ maxNetworksCount := 2
+
+ for i := 0; i < maxNetworksCount; i++ {
+ n, err := network.New(ctx)
+ assert.Nil(t, err)
+ // use t.Cleanup to run after terminateContainerOnEnd
+ t.Cleanup(func() {
+ assert.NoError(t, n.Remove(ctx))
+ })
+
+ networks = append(networks, n.Name)
+ }
+
+ req := testcontainers.ContainerRequest{
+ Image: nginxAlpineImage,
+ ExposedPorts: []string{nginxDefaultPort},
+ WaitingFor: wait.ForAll(
+ wait.ForListeningPort(nginxDefaultPort),
+ wait.ForLog("Configuration complete; ready for start up"),
+ ),
+ Networks: networks,
+ }
+
+ nginx, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
+ ContainerRequest: req,
+ Started: true,
+ })
+
+ require.NoError(t, err)
+ defer func() {
+ require.NoError(t, nginx.Terminate(ctx))
+ }()
+
+ containerId := nginx.GetContainerID()
+
+ cli, err := testcontainers.NewDockerClientWithOpts(ctx)
+ assert.Nil(t, err)
+ defer cli.Close()
+
+ cnt, err := cli.ContainerInspect(ctx, containerId)
+ assert.Nil(t, err)
+ assert.Equal(t, maxNetworksCount, len(cnt.NetworkSettings.Networks))
+ assert.NotNil(t, cnt.NetworkSettings.Networks[networks[0]])
+ assert.NotNil(t, cnt.NetworkSettings.Networks[networks[1]])
+}
+
+func TestMultipleContainersInTheNewNetwork(t *testing.T) {
+ ctx := context.Background()
+
+ net, err := network.New(ctx, network.WithCheckDuplicate(), network.WithDriver("bridge"))
+ if err != nil {
+ t.Fatal("cannot create network")
+ }
+ defer func() {
+ require.NoError(t, net.Remove(ctx))
+ }()
+
+ networkName := net.Name
+
+ req1 := testcontainers.ContainerRequest{
+ Image: nginxAlpineImage,
+ Networks: []string{networkName},
+ }
+
+ c1, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
+ ContainerRequest: req1,
+ Started: true,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ require.NoError(t, c1.Terminate(ctx))
+ }()
+
+ req2 := testcontainers.ContainerRequest{
+ Image: nginxAlpineImage,
+ Networks: []string{networkName},
+ }
+ c2, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
+ ContainerRequest: req2,
+ Started: true,
+ })
+ if err != nil {
+ t.Fatal(err)
+ return
+ }
+ defer func() {
+ require.NoError(t, c2.Terminate(ctx))
+ }()
+
+ pNets, err := c1.Networks(ctx)
+ require.NoError(t, err)
+
+ rNets, err := c2.Networks(ctx)
+ require.NoError(t, err)
+
+ assert.Equal(t, 1, len(pNets))
+ assert.Equal(t, 1, len(rNets))
+
+ assert.Equal(t, networkName, pNets[0])
+ assert.Equal(t, networkName, rNets[0])
+}
+
+func TestNew_withOptions(t *testing.T) {
+ // newNetworkWithOptions {
+ ctx := context.Background()
+
+ // dockernetwork is the alias used for github.com/docker/docker/api/types/network
+ ipamConfig := dockernetwork.IPAM{
+ Driver: "default",
+ Config: []dockernetwork.IPAMConfig{
+ {
+ Subnet: "10.1.1.0/24",
+ Gateway: "10.1.1.254",
+ },
+ },
+ Options: map[string]string{
+ "driver": "host-local",
+ },
+ }
+ net, err := network.New(ctx,
+ network.WithCheckDuplicate(),
+ network.WithIPAM(&ipamConfig),
+ network.WithAttachable(),
+ network.WithDriver("bridge"),
+ )
+ // }
+ if err != nil {
+ t.Fatal("cannot create network: ", err)
+ }
+ defer func() {
+ require.NoError(t, net.Remove(ctx))
+ }()
+
+ networkName := net.Name
+
+ nginx, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{
+ Image: "nginx:alpine",
+ ExposedPorts: []string{
+ "80/tcp",
+ },
+ Networks: []string{
+ networkName,
+ },
+ },
+ })
+ require.NoError(t, err)
+ defer func() {
+ require.NoError(t, nginx.Terminate(ctx))
+ }()
+
+ provider, err := testcontainers.ProviderDocker.GetProvider()
+ if err != nil {
+ t.Fatal("Cannot get Provider")
+ }
+ defer provider.Close()
+
+ //nolint:staticcheck
+ foundNetwork, err := provider.GetNetwork(ctx, testcontainers.NetworkRequest{Name: networkName})
+ if err != nil {
+ t.Fatal("Cannot get created network by name")
+ }
+ assert.Equal(t, ipamConfig, foundNetwork.IPAM)
+}
+
+func TestWithNetwork(t *testing.T) {
+ // first create the network to be reused
+ nw, err := network.New(context.Background(), network.WithCheckDuplicate(), network.WithLabels(map[string]string{"network-type": "unique"}))
+ require.NoError(t, err)
+ defer func() {
+ require.NoError(t, nw.Remove(context.Background()))
+ }()
+
+ networkName := nw.Name
+
+ // check that the network is reused, multiple times
+ for i := 0; i < 100; i++ {
+ req := testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{},
+ }
+
+ network.WithNetwork([]string{"alias"}, nw)(&req)
+
+ assert.Equal(t, 1, len(req.Networks))
+ assert.Equal(t, networkName, req.Networks[0])
+
+ assert.Equal(t, 1, len(req.NetworkAliases))
+ assert.Equal(t, map[string][]string{networkName: {"alias"}}, req.NetworkAliases)
+ }
+
+ // verify that the network is created only once
+ client, err := testcontainers.NewDockerClientWithOpts(context.Background())
+ require.NoError(t, err)
+
+ args := filters.NewArgs()
+ args.Add("name", networkName)
+
+ resources, err := client.NetworkList(context.Background(), types.NetworkListOptions{
+ Filters: args,
+ })
+ require.NoError(t, err)
+ assert.Len(t, resources, 1)
+
+ newNetwork := resources[0]
+
+ expectedLabels := testcontainers.GenericLabels()
+ expectedLabels["network-type"] = "unique"
+
+ assert.Equal(t, networkName, newNetwork.Name)
+ assert.False(t, newNetwork.Attachable)
+ assert.False(t, newNetwork.Internal)
+ assert.Equal(t, expectedLabels, newNetwork.Labels)
+}
+
+func TestWithSyntheticNetwork(t *testing.T) {
+ nw := &testcontainers.DockerNetwork{
+ Name: "synthetic-network",
+ }
+
+ networkName := nw.Name
+
+ req := testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{
+ Image: nginxAlpineImage,
+ },
+ }
+
+ network.WithNetwork([]string{"alias"}, nw)(&req)
+
+ assert.Equal(t, 1, len(req.Networks))
+ assert.Equal(t, networkName, req.Networks[0])
+
+ assert.Equal(t, 1, len(req.NetworkAliases))
+ assert.Equal(t, map[string][]string{networkName: {"alias"}}, req.NetworkAliases)
+
+ // verify that the network is created only once
+ client, err := testcontainers.NewDockerClientWithOpts(context.Background())
+ require.NoError(t, err)
+
+ args := filters.NewArgs()
+ args.Add("name", networkName)
+
+ resources, err := client.NetworkList(context.Background(), types.NetworkListOptions{
+ Filters: args,
+ })
+ require.NoError(t, err)
+ assert.Len(t, resources, 0) // no Docker network was created
+
+ c, err := testcontainers.GenericContainer(context.Background(), req)
+ require.NoError(t, err)
+ assert.NotNil(t, c)
+ defer func() {
+ require.NoError(t, c.Terminate(context.Background()))
+ }()
+}
+
+func TestWithNewNetwork(t *testing.T) {
+ req := testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{},
+ }
+
+ network.WithNewNetwork(context.Background(), []string{"alias"},
+ network.WithAttachable(),
+ network.WithInternal(),
+ network.WithLabels(map[string]string{"this-is-a-test": "value"}),
+ )(&req)
+
+ assert.Equal(t, 1, len(req.Networks))
+
+ networkName := req.Networks[0]
+
+ assert.Equal(t, 1, len(req.NetworkAliases))
+ assert.Equal(t, map[string][]string{networkName: {"alias"}}, req.NetworkAliases)
+
+ client, err := testcontainers.NewDockerClientWithOpts(context.Background())
+ require.NoError(t, err)
+
+ args := filters.NewArgs()
+ args.Add("name", networkName)
+
+ resources, err := client.NetworkList(context.Background(), types.NetworkListOptions{
+ Filters: args,
+ })
+ require.NoError(t, err)
+ assert.Len(t, resources, 1)
+
+ newNetwork := resources[0]
+ defer func() {
+ require.NoError(t, client.NetworkRemove(context.Background(), newNetwork.ID))
+ }()
+
+ expectedLabels := testcontainers.GenericLabels()
+ expectedLabels["this-is-a-test"] = "value"
+
+ assert.Equal(t, networkName, newNetwork.Name)
+ assert.True(t, newNetwork.Attachable)
+ assert.True(t, newNetwork.Internal)
+ assert.Equal(t, expectedLabels, newNetwork.Labels)
+}
+
+func TestWithNewNetworkContextTimeout(t *testing.T) {
+ req := testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{},
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
+ defer cancel()
+
+ network.WithNewNetwork(ctx, []string{"alias"},
+ network.WithAttachable(),
+ network.WithInternal(),
+ network.WithLabels(map[string]string{"this-is-a-test": "value"}),
+ )(&req)
+
+ // we do not want to fail, just skip the network creation
+ assert.Equal(t, 0, len(req.Networks))
+ assert.Equal(t, 0, len(req.NetworkAliases))
+}
diff --git a/network_test.go b/network_test.go
deleted file mode 100644
index 4b2c1e6e28..0000000000
--- a/network_test.go
+++ /dev/null
@@ -1,193 +0,0 @@
-package testcontainers
-
-import (
- "context"
- "fmt"
- "log"
- "testing"
- "time"
-
- "github.com/docker/docker/api/types/network"
- "github.com/stretchr/testify/assert"
-
- "github.com/testcontainers/testcontainers-go/wait"
-)
-
-// Create a network using a provider. By default it is Docker.
-func ExampleNetworkProvider_CreateNetwork() {
- // createNetwork {
- ctx := context.Background()
- networkName := "new-generic-network"
- net, _ := GenericNetwork(ctx, GenericNetworkRequest{
- NetworkRequest: NetworkRequest{
- Name: networkName,
- CheckDuplicate: true,
- },
- })
- defer func() {
- if err := net.Remove(ctx); err != nil {
- panic(err)
- }
- }()
- // }
-
- nginxC, _ := GenericContainer(ctx, GenericContainerRequest{
- ContainerRequest: ContainerRequest{
- Image: "nginx",
- ExposedPorts: []string{
- "80/tcp",
- },
- Networks: []string{
- networkName,
- },
- },
- Started: true,
- })
- defer func() {
- if err := nginxC.Terminate(ctx); err != nil {
- log.Fatalf("failed to terminate container: %s", err)
- }
- }()
-
- nginxC.GetContainerID()
-
- state, err := nginxC.State(ctx)
- if err != nil {
- panic(err)
- }
-
- fmt.Println(state.Running)
-
- // Output:
- // true
-}
-
-func Test_NetworkWithIPAM(t *testing.T) {
- // withIPAM {
- ctx := context.Background()
- networkName := "test-network-with-ipam"
- ipamConfig := network.IPAM{
- Driver: "default",
- Config: []network.IPAMConfig{
- {
- Subnet: "10.1.1.0/24",
- Gateway: "10.1.1.254",
- },
- },
- Options: map[string]string{
- "driver": "host-local",
- },
- }
- net, err := GenericNetwork(ctx, GenericNetworkRequest{
- NetworkRequest: NetworkRequest{
- Name: networkName,
- CheckDuplicate: true,
- IPAM: &ipamConfig,
- },
- })
- // }
- if err != nil {
- t.Fatal("cannot create network: ", err)
- }
-
- defer func() {
- _ = net.Remove(ctx)
- }()
-
- nginxC, _ := GenericContainer(ctx, GenericContainerRequest{
- ContainerRequest: ContainerRequest{
- Image: "nginx",
- ExposedPorts: []string{
- "80/tcp",
- },
- Networks: []string{
- networkName,
- },
- },
- })
- terminateContainerOnEnd(t, ctx, nginxC)
- nginxC.GetContainerID()
-
- provider, err := ProviderDocker.GetProvider()
- if err != nil {
- t.Fatal("Cannot get Provider")
- }
- defer provider.Close()
-
- foundNetwork, err := provider.GetNetwork(ctx, NetworkRequest{Name: networkName})
- if err != nil {
- t.Fatal("Cannot get created network by name")
- }
- assert.Equal(t, ipamConfig, foundNetwork.IPAM)
-}
-
-func Test_MultipleContainersInTheNewNetwork(t *testing.T) {
- ctx := context.Background()
-
- networkName := "test-network"
-
- networkRequest := NetworkRequest{
- Driver: "bridge",
- Name: networkName,
- Attachable: true,
- }
-
- env := make(map[string]string)
- env["POSTGRES_PASSWORD"] = "Password1"
- dbContainerRequest := ContainerRequest{
- Image: "postgres:12",
- ExposedPorts: []string{"5432/tcp"},
- AutoRemove: true,
- Env: env,
- WaitingFor: wait.ForListeningPort("5432/tcp"),
- Networks: []string{networkName},
- }
-
- net, err := GenericNetwork(ctx, GenericNetworkRequest{
- NetworkRequest: networkRequest,
- })
- if err != nil {
- t.Fatal("cannot create network")
- }
-
- defer func() {
- _ = net.Remove(ctx)
- }()
-
- postgres, err := GenericContainer(ctx, GenericContainerRequest{
- ContainerRequest: dbContainerRequest,
- Started: true,
- })
- if err != nil {
- t.Fatal(err)
- }
-
- terminateContainerOnEnd(t, ctx, postgres)
-
- env = make(map[string]string)
- env["RABBITMQ_ERLANG_COOKIE"] = "f2a2d3d27c75"
- env["RABBITMQ_DEFAULT_USER"] = "admin"
- env["RABBITMQ_DEFAULT_PASS"] = "Password1"
- hp := wait.ForListeningPort("5672/tcp")
- hp.WithStartupTimeout(3 * time.Minute)
- amqpRequest := ContainerRequest{
- Image: "rabbitmq:3.8.19-management-alpine",
- ExposedPorts: []string{"15672/tcp", "5672/tcp"},
- Env: env,
- AutoRemove: true,
- WaitingFor: hp,
- Networks: []string{networkName},
- }
- rabbitmq, err := GenericContainer(ctx, GenericContainerRequest{
- ContainerRequest: amqpRequest,
- Started: true,
- })
- if err != nil {
- t.Fatal(err)
- return
- }
-
- terminateContainerOnEnd(t, ctx, rabbitmq)
- fmt.Println(postgres.GetContainerID())
- fmt.Println(rabbitmq.GetContainerID())
-}
diff --git a/options.go b/options.go
index 7317e41b40..0b13764a44 100644
--- a/options.go
+++ b/options.go
@@ -3,7 +3,6 @@ package testcontainers
import (
"context"
"fmt"
- "strings"
"time"
"dario.cat/mergo"
@@ -132,36 +131,6 @@ func WithImageSubstitutors(fn ...ImageSubstitutor) CustomizeRequestOption {
}
}
-// 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, including options,
// as part of the different lifecycle hooks.
type Executable interface {
diff --git a/options_test.go b/options_test.go
index be02a37edd..09da87286b 100644
--- a/options_test.go
+++ b/options_test.go
@@ -5,8 +5,6 @@ import (
"io"
"testing"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/api/types/filters"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -69,57 +67,6 @@ func TestOverrideContainerRequest(t *testing.T) {
assert.Equal(t, wait.ForLog("foo"), req.WaitingFor)
}
-func TestWithNetwork(t *testing.T) {
- req := testcontainers.GenericContainerRequest{
- ContainerRequest: testcontainers.ContainerRequest{},
- }
-
- testcontainers.WithNetwork("new-network", "alias")(&req)
-
- assert.Equal(t, []string{"new-network"}, req.Networks)
- assert.Equal(t, map[string][]string{"new-network": {"alias"}}, req.NetworkAliases)
-
- client, err := testcontainers.NewDockerClientWithOpts(context.Background())
- require.NoError(t, err)
-
- args := filters.NewArgs()
- args.Add("name", "new-network")
-
- resources, err := client.NetworkList(context.Background(), types.NetworkListOptions{
- Filters: args,
- })
- require.NoError(t, err)
- assert.Len(t, resources, 1)
-
- assert.Equal(t, "new-network", resources[0].Name)
-}
-
-func TestWithNetworkMultipleCallsWithSameNameReuseTheNetwork(t *testing.T) {
- for int := 0; int < 100; int++ {
- req := testcontainers.GenericContainerRequest{
- ContainerRequest: testcontainers.ContainerRequest{},
- }
-
- testcontainers.WithNetwork("new-network", "alias")(&req)
- assert.Equal(t, []string{"new-network"}, req.Networks)
- assert.Equal(t, map[string][]string{"new-network": {"alias"}}, req.NetworkAliases)
- }
-
- client, err := testcontainers.NewDockerClientWithOpts(context.Background())
- require.NoError(t, err)
-
- args := filters.NewArgs()
- args.Add("name", "new-network")
-
- resources, err := client.NetworkList(context.Background(), types.NetworkListOptions{
- Filters: args,
- })
- require.NoError(t, err)
- assert.Len(t, resources, 1)
-
- assert.Equal(t, "new-network", resources[0].Name)
-}
-
func TestWithStartupCommand(t *testing.T) {
req := testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{