Skip to content

Commit

Permalink
Merge branch 'main' into user-defined-lifecycle-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
mdelapenya authored Dec 18, 2023
2 parents 497c226 + 2120c33 commit 1d3e1d1
Show file tree
Hide file tree
Showing 19 changed files with 834 additions and 524 deletions.
1 change: 1 addition & 0 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
217 changes: 14 additions & 203 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 @@ -1298,21 +1169,34 @@ 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)
}
if c != 1 {
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)
}
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 {
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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)))
Expand Down
15 changes: 13 additions & 2 deletions docs/features/common_functional_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>

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 <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>

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

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-->
24 changes: 21 additions & 3 deletions docs/features/override_container_command.md
Original file line number Diff line number Diff line change
@@ -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{
Expand All @@ -13,3 +15,19 @@ req := ContainerRequest{
}
```

## Executing a command

You can execute a command inside a running container, similar to a `docker exec` call:

<!--codeinclude-->
[Executing a command](../../docker_test.go) inside_block:exec_example
<!--/codeinclude-->

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:


<!--codeinclude-->
[Command logs](../../docker_test.go) inside_block:exec_reader_example
<!--/codeinclude-->

This is done this way, because it brings more flexibility to the user, rather than returning a string.
Loading

0 comments on commit 1d3e1d1

Please sign in to comment.