Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: create networks with random names #1993

Merged
1 change: 1 addition & 0 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,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
Expand Down
202 changes: 0 additions & 202 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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")
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -1812,66 +1683,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")
Expand Down Expand Up @@ -2105,19 +1916,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)))
Expand Down
31 changes: 24 additions & 7 deletions docs/features/creating_networks.md
Original file line number Diff line number Diff line change
@@ -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 <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>

<!--codeinclude-->
[Creating a network](../../network_test.go) inside_block:createNetwork
<!--/codeinclude-->
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

<!--codeinclude-->
[Creating a network with IPAM](../../network_test.go) inside_block:withIPAM
<!--/codeinclude-->
[Creating a network](../../network/network_test.go) inside_block:createNetwork
[Creating a network with options](../../network/network_test.go) inside_block:newNetworkWithOptions
<!--/codeinclude-->
4 changes: 3 additions & 1 deletion docs/features/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!--codeinclude-->
[Creating custom networks](../../docker_test.go) inside_block:testNetworkAliases
[Creating custom networks](../../network/network_test.go) inside_block:testNetworkAliases
<!--/codeinclude-->
2 changes: 1 addition & 1 deletion examples/toxiproxy/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
23 changes: 12 additions & 11 deletions examples/toxiproxy/toxiproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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())
Expand Down
2 changes: 2 additions & 0 deletions generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down