diff --git a/docker.go b/docker.go index 08fc22572b..1721ad667c 100644 --- a/docker.go +++ b/docker.go @@ -35,6 +35,11 @@ import ( // Implement interfaces var _ Container = (*DockerContainer)(nil) +const ( + Bridge = "bridge" // Bridge network name (as well as driver) + ReaperDefault = "reaper_default" // Default network name when bridge is not available +) + // DockerContainer represents a container started using Docker type DockerContainer struct { // Container ID from Docker @@ -429,8 +434,9 @@ func (n *DockerNetwork) Remove(ctx context.Context) error { // DockerProvider implements the ContainerProvider interface type DockerProvider struct { - client *client.Client - hostCache string + client *client.Client + hostCache string + defaultNetwork string // default container network } var _ ContainerProvider = (*DockerProvider)(nil) @@ -486,6 +492,31 @@ func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (st // CreateContainer fulfills a request for a container without starting it func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerRequest) (Container, error) { + var err error + + // Make sure that bridge network exists + // In case it is disabled we will create reaper_default network + p.defaultNetwork, err = getDefaultNetwork(ctx, p.client) + if err != nil { + return nil, err + } + + // If default network is not bridge make sure it is attached to the request + // as container won't be attached to it automatically + if p.defaultNetwork != Bridge { + isAttached := false + for _, net := range req.Networks { + if net == p.defaultNetwork { + isAttached = true + break + } + } + + if !isAttached { + req.Networks = append(req.Networks, p.defaultNetwork) + } + } + exposedPortSet, exposedPortMap, err := nat.ParsePortSpecs(req.ExposedPorts) if err != nil { return nil, err @@ -721,6 +752,12 @@ func (p *DockerProvider) daemonHost(ctx context.Context) (string, error) { // 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 + + // Make sure that bridge network exists + // In case it is disabled we will create reaper_default network + p.defaultNetwork, err = getDefaultNetwork(ctx, p.client) + if req.Labels == nil { req.Labels = make(map[string]string) } @@ -782,7 +819,8 @@ func (p *DockerProvider) GetNetwork(ctx context.Context, req NetworkRequest) (ty } func (p *DockerProvider) GetGatewayIP(ctx context.Context) (string, error) { - nw, err := p.GetNetwork(ctx, NetworkRequest{Name: "bridge"}) + // Use a default network as defined in the DockerProvider + nw, err := p.GetNetwork(ctx, NetworkRequest{Name: p.defaultNetwork}) if err != nil { return "", err } @@ -824,3 +862,42 @@ func getDefaultGatewayIP() (string, error) { } return string(ip), nil } + +func getDefaultNetwork(ctx context.Context, cli *client.Client) (string, error) { + // Get list of available networks + networkResources, err := cli.NetworkList(ctx, types.NetworkListOptions{}) + if err != nil { + return "", err + } + + reaperNetwork := ReaperDefault + + reaperNetworkExists := false + + for _, net := range networkResources { + if net.Name == Bridge { + return Bridge, nil + } + + if net.Name == reaperNetwork { + reaperNetworkExists = true + } + } + + // Create a bridge network for the container communications + if !reaperNetworkExists { + _, err = cli.NetworkCreate(ctx, reaperNetwork, types.NetworkCreate{ + Driver: Bridge, + Attachable: true, + Labels: map[string]string{ + TestcontainerLabel: "true", + }, + }) + + if err != nil { + return "", err + } + } + + return reaperNetwork, nil +} diff --git a/docker_test.go b/docker_test.go index 5f7a12ddd5..507a9a9e7c 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1342,3 +1342,32 @@ func TestDockerContainerCopyFileToContainer(t *testing.T) { t.Fatalf("File %s should exist, expected return code 0, got %v", copiedFileName, c) } } + +func TestContainerWithReaperNetwork(t *testing.T) { + ctx := context.Background() + req := ContainerRequest{ + Image: "nginx", + ExposedPorts: []string{"80/tcp"}, + WaitingFor: wait.ForAll( + wait.ForListeningPort("80/tcp"), + wait.ForLog("Configuration complete; ready for start up"), + ), + } + + nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + + if err != nil { + t.Fatal(err) + } + + defer func() { + t.Log("terminating container") + err := nginxC.Terminate(ctx) + if err != nil { + t.Fatal(err) + } + }() +} diff --git a/reaper.go b/reaper.go index 8a4d0ca0a6..be9818f229 100644 --- a/reaper.go +++ b/reaper.go @@ -70,6 +70,11 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, r WaitingFor: wait.ForListeningPort(listeningPort), } + // Attach reaper container to a requested network if it is specified + if p, ok := provider.(*DockerProvider); ok { + req.Networks = append(req.Networks, p.defaultNetwork) + } + c, err := provider.RunContainer(ctx, req) if err != nil { return nil, err