Skip to content

Commit

Permalink
feat: allow setting custom IP address for container
Browse files Browse the repository at this point in the history
this is possible only when using user defined subnets
  • Loading branch information
oktalz committed Oct 26, 2022
1 parent b2ab07d commit eda8f0e
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 8 deletions.
2 changes: 2 additions & 0 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/pkg/archive"
"github.com/docker/go-connections/nat"

Expand Down Expand Up @@ -120,6 +121,7 @@ type ContainerRequest struct {
ShmSize int64 // Amount of memory shared with the host (in bytes)
CapAdd []string // Add Linux capabilities
CapDrop []string // Drop Linux capabilities
IPAMConfig *network.EndpointIPAMConfig
}

type (
Expand Down
10 changes: 5 additions & 5 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ func (c *DockerContainer) inspectContainer(ctx context.Context) (*types.Containe
// Logs will fetch both STDOUT and STDERR from the current container. Returns a
// ReadCloser and leaves it up to the caller to extract what it wants.
func (c *DockerContainer) Logs(ctx context.Context) (io.ReadCloser, error) {

const streamHeaderSize = 8

options := types.ContainerLogsOptions{
Expand Down Expand Up @@ -810,7 +809,7 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error
}

func (p *DockerProvider) logDockerServerInfo() {
infoMessage := `%v - Connected to docker:
infoMessage := `%v - Connected to docker:
Server Version: %v
API Version: %v
Operating System: %v
Expand Down Expand Up @@ -1032,7 +1031,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
if err != nil {
return nil, err
}
for p, _ := range image.ContainerConfig.ExposedPorts {
for p := range image.ContainerConfig.ExposedPorts {
exposedPorts = append(exposedPorts, string(p))
}
}
Expand Down Expand Up @@ -1084,8 +1083,9 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
})
if err == nil {
endpointSetting := network.EndpointSettings{
Aliases: req.NetworkAliases[attachContainerTo],
NetworkID: nw.ID,
Aliases: req.NetworkAliases[attachContainerTo],
NetworkID: nw.ID,
IPAMConfig: req.IPAMConfig,
}
endpointConfigs[attachContainerTo] = &endpointSetting
}
Expand Down
112 changes: 109 additions & 3 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@ import (
"database/sql"
"errors"
"fmt"
// Import mysql into the scope of this package (required)
_ "github.com/go-sql-driver/mysql"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/netip"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"

// Import mysql into the scope of this package (required)
_ "github.com/go-sql-driver/mysql"

"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/go-units"
"github.com/go-redis/redis/v8"
Expand Down Expand Up @@ -130,6 +133,110 @@ func TestContainerAttachedToNewNetwork(t *testing.T) {
}
}

func TestContainerAttachedToNewNetworkWithFixedIP(t *testing.T) {
// in order to keep this test working on every setup,
// considering docker network available ranges can differ on different machines
// first we will create a network, pick gateway and subnet
// of existing network, destroy that network, and recreate it with same params
// this is needed for next part of the test,
// since docker complains if network is not user configured
// "user specified IP address is supported only when connecting to networks with user configured subnets"
networkName := "network-fip"
ctx := context.Background()
gcr := GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: ContainerRequest{
Image: nginxAlpineImage,
ExposedPorts: []string{
nginxDefaultPort,
},
Networks: []string{
networkName,
},
},
Started: true,
}
gnr := GenericNetworkRequest{
ProviderType: providerType,
NetworkRequest: NetworkRequest{
Name: networkName,
CheckDuplicate: true,
},
}

newNetwork, err := GenericNetwork(ctx, gnr)
if err != nil {
t.Fatal(err)
}
provider, err := gnr.ProviderType.GetProvider()
require.NoError(t, err)
networkResource, err := provider.GetNetwork(ctx, gnr.NetworkRequest)
require.NoError(t, err)

if len(networkResource.IPAM.Config) == 0 {
t.Errorf("Expected an IPAM config, got %v", networkResource.IPAM.Config)
}
subnet := networkResource.IPAM.Config[0].Subnet
gw := networkResource.IPAM.Config[0].Gateway

err = newNetwork.Remove(ctx)
require.NoError(t, err)
gnr.IPAM = &network.IPAM{
Config: []network.IPAMConfig{
{
Gateway: gw,
Subnet: subnet,
},
},
}
newNetwork, err = GenericNetwork(ctx, gnr)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
require.NoError(t, newNetwork.Remove(ctx))
})

// first try to create container without defining custom IP
nginx, err := GenericContainer(ctx, gcr)

require.NoError(t, err)
terminateContainerOnEnd(t, ctx, nginx)

networkIP, err := nginx.ContainerIP(ctx)
if err != nil {
t.Fatal(err)
}
if len(networkIP) == 0 {
t.Errorf("Expected an IP address, got %v", networkIP)
}
ip, err := netip.ParseAddr(networkIP)
if err != nil {
t.Fatal(err)
}

nextIP := ip.Next()
nextIPStr := nextIP.String()

gcr.IPAMConfig = &network.EndpointIPAMConfig{
IPv4Address: nextIPStr,
}
// try to create container with custom IP
nginx2, err := GenericContainer(ctx, gcr)
require.NoError(t, err)
terminateContainerOnEnd(t, ctx, nginx2)
networkIP, err = nginx2.ContainerIP(ctx)
if err != nil {
t.Fatal(err)
}
if len(networkIP) == 0 {
t.Errorf("Expected an IP address, got %v", networkIP)
}
if networkIP != nextIPStr {
t.Errorf("ip address is not the same as requested one, Expected %s, got %s", nextIPStr, networkIP)
}
}

func TestContainerWithHostNetworkOptions(t *testing.T) {
absPath, err := filepath.Abs("./testresources/nginx-highport.conf")
if err != nil {
Expand Down Expand Up @@ -1565,7 +1672,6 @@ func TestReadTCPropsFile(t *testing.T) {
config := configureTC()

assert.Equal(t, tt.expected, config, "Configuration doesn't not match")

})
}
})
Expand Down

0 comments on commit eda8f0e

Please sign in to comment.