Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions docker/api/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .. import errors
from .. import utils
from ..utils.utils import create_networking_config, create_endpoint_config


class ContainerApiMixin(object):
Expand Down Expand Up @@ -98,7 +99,7 @@ def create_container(self, image, command=None, hostname=None, user=None,
cpu_shares=None, working_dir=None, domainname=None,
memswap_limit=None, cpuset=None, host_config=None,
mac_address=None, labels=None, volume_driver=None,
stop_signal=None):
stop_signal=None, networking_config=None):

if isinstance(volumes, six.string_types):
volumes = [volumes, ]
Expand All @@ -113,7 +114,7 @@ def create_container(self, image, command=None, hostname=None, user=None,
tty, mem_limit, ports, environment, dns, volumes, volumes_from,
network_disabled, entrypoint, cpu_shares, working_dir, domainname,
memswap_limit, cpuset, host_config, mac_address, labels,
volume_driver, stop_signal
volume_driver, stop_signal, networking_config,
)
return self.create_container_from_config(config, name)

Expand All @@ -139,6 +140,12 @@ def create_host_config(self, *args, **kwargs):
kwargs['version'] = self._version
return utils.create_host_config(*args, **kwargs)

def create_networking_config(self, *args, **kwargs):
return create_networking_config(*args, **kwargs)

def create_endpoint_config(self, *args, **kwargs):
return create_endpoint_config(self._version, *args, **kwargs)

@utils.check_resource
def diff(self, container):
return self._result(
Expand Down
9 changes: 7 additions & 2 deletions docker/api/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ def inspect_network(self, net_id):

@check_resource
@minimum_version('1.21')
def connect_container_to_network(self, container, net_id):
data = {"container": container}
def connect_container_to_network(self, container, net_id, aliases=None):
data = {
"Container": container,
"EndpointConfig": {
"Aliases": aliases,
},
}
url = self._url("/networks/{0}/connect", net_id)
self._post_json(url, data=data)

Expand Down
2 changes: 1 addition & 1 deletion docker/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
kwargs_from_env, convert_filters, datetime_to_timestamp, create_host_config,
create_container_config, parse_bytes, ping_registry, parse_env_file,
version_lt, version_gte, decode_json_header, split_command,
create_ipam_config, create_ipam_pool
create_ipam_config, create_ipam_pool,
) # flake8: noqa

from .types import Ulimit, LogConfig # flake8: noqa
Expand Down
23 changes: 22 additions & 1 deletion docker/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,26 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
return host_config


def create_networking_config(endpoints_config=None):
networking_config = {}

if endpoints_config:
networking_config["EndpointsConfig"] = endpoints_config

return networking_config


def create_endpoint_config(version, aliases=None):
endpoint_config = {}

if aliases:
if version_lt(version, '1.22'):
raise host_config_version_error('endpoint_config.aliases', '1.22')
endpoint_config["Aliases"] = aliases

return endpoint_config


def parse_env_file(env_file):
"""
Reads a line-separated environment file.
Expand Down Expand Up @@ -752,7 +772,7 @@ def create_container_config(
dns=None, volumes=None, volumes_from=None, network_disabled=False,
entrypoint=None, cpu_shares=None, working_dir=None, domainname=None,
memswap_limit=None, cpuset=None, host_config=None, mac_address=None,
labels=None, volume_driver=None, stop_signal=None
labels=None, volume_driver=None, stop_signal=None, networking_config=None,
):
if isinstance(command, six.string_types):
command = split_command(command)
Expand Down Expand Up @@ -878,6 +898,7 @@ def create_container_config(
'WorkingDir': working_dir,
'MemorySwap': memswap_limit,
'HostConfig': host_config,
'NetworkingConfig': networking_config,
'MacAddress': mac_address,
'Labels': labels,
'VolumeDriver': volume_driver,
Expand Down
46 changes: 45 additions & 1 deletion tests/integration/network_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
from ..base import requires_api_version


@requires_api_version('1.21')
class TestNetworks(helpers.BaseTestCase):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about leaving this here, and only adding requires('1.22') to the few methods where it's necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure if the two would interact weirdly, so I elected to be more explicit, but I don't have strong feelings about it

def create_network(self, *args, **kwargs):
net_name = u'dockerpy{}'.format(random.getrandbits(24))[:14]
net_id = self.client.create_network(net_name, *args, **kwargs)['Id']
self.tmp_networks.append(net_id)
return (net_name, net_id)

@requires_api_version('1.21')
def test_list_networks(self):
networks = self.client.networks()
initial_size = len(networks)
Expand All @@ -31,6 +31,7 @@ def test_list_networks(self):
networks_by_partial_id = self.client.networks(ids=[net_id[:8]])
self.assertEqual([n['Id'] for n in networks_by_partial_id], [net_id])

@requires_api_version('1.21')
def test_inspect_network(self):
net_name, net_id = self.create_network()

Expand All @@ -41,12 +42,14 @@ def test_inspect_network(self):
self.assertEqual(net['Scope'], 'local')
self.assertEqual(net['IPAM']['Driver'], 'default')

@requires_api_version('1.21')
def test_create_network_with_host_driver_fails(self):
net_name = 'dockerpy{}'.format(random.getrandbits(24))[:14]

with pytest.raises(docker.errors.APIError):
self.client.create_network(net_name, driver='host')

@requires_api_version('1.21')
def test_remove_network(self):
initial_size = len(self.client.networks())

Expand All @@ -56,6 +59,7 @@ def test_remove_network(self):
self.client.remove_network(net_id)
self.assertEqual(len(self.client.networks()), initial_size)

@requires_api_version('1.21')
def test_connect_and_disconnect_container(self):
net_name, net_id = self.create_network()

Expand All @@ -76,6 +80,22 @@ def test_connect_and_disconnect_container(self):
network_data = self.client.inspect_network(net_id)
self.assertFalse(network_data.get('Containers'))

@requires_api_version('1.22')
def test_connect_with_aliases(self):
net_name, net_id = self.create_network()

container = self.client.create_container('busybox', 'top')
self.tmp_containers.append(container)
self.client.start(container)

self.client.connect_container_to_network(
container, net_id, aliases=['foo', 'bar'])
container_data = self.client.inspect_container(container)
self.assertEqual(
container_data['NetworkSettings']['Networks'][net_name]['Aliases'],
['foo', 'bar'])

@requires_api_version('1.21')
def test_connect_on_container_create(self):
net_name, net_id = self.create_network()

Expand All @@ -95,3 +115,27 @@ def test_connect_on_container_create(self):
self.client.disconnect_container_from_network(container, net_id)
network_data = self.client.inspect_network(net_id)
self.assertFalse(network_data.get('Containers'))

@requires_api_version('1.22')
def test_create_with_aliases(self):
net_name, net_id = self.create_network()

container = self.client.create_container(
image='busybox',
command='top',
host_config=self.client.create_host_config(
network_mode=net_name,
),
networking_config=self.client.create_networking_config({
net_name: self.client.create_endpoint_config(
aliases=['foo', 'bar'],
),
}),
)
self.tmp_containers.append(container)
self.client.start(container)

container_data = self.client.inspect_container(container)
self.assertEqual(
container_data['NetworkSettings']['Networks'][net_name]['Aliases'],
['foo', 'bar'])
33 changes: 33 additions & 0 deletions tests/unit/container_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import six

from . import fake_api
from ..base import requires_api_version
from .api_test import (
DockerClientTest, url_prefix, fake_request, DEFAULT_TIMEOUT_SECONDS,
fake_inspect_container
Expand Down Expand Up @@ -983,6 +984,38 @@ def test_create_container_with_stop_signal(self):
self.assertEqual(args[1]['headers'],
{'Content-Type': 'application/json'})

@requires_api_version('1.22')
def test_create_container_with_aliases(self):
self.client.create_container(
'busybox', 'ls',
host_config=self.client.create_host_config(
network_mode='some-network',
),
networking_config=self.client.create_networking_config({
'some-network': self.client.create_endpoint_config(
aliases=['foo', 'bar'],
),
}),
)

args = fake_request.call_args
self.assertEqual(json.loads(args[1]['data']),
json.loads('''
{"Tty": false, "Image": "busybox",
"Cmd": ["ls"], "AttachStdin": false,
"AttachStderr": true,
"AttachStdout": true, "OpenStdin": false,
"StdinOnce": false,
"NetworkDisabled": false,
"HostConfig": {
"NetworkMode": "some-network"
},
"NetworkingConfig": {
"EndpointsConfig": {
"some-network": {"Aliases": ["foo", "bar"]}
}
}}'''))


class ContainerTest(DockerClientTest):
def test_list_containers(self):
Expand Down
12 changes: 10 additions & 2 deletions tests/unit/network_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,23 @@ def test_connect_container_to_network(self):

with mock.patch('docker.Client.post', post):
self.client.connect_container_to_network(
{'Id': container_id}, network_id)
{'Id': container_id},
network_id,
aliases=['foo', 'bar'],
)

self.assertEqual(
post.call_args[0][0],
url_prefix + 'networks/{0}/connect'.format(network_id))

self.assertEqual(
json.loads(post.call_args[1]['data']),
{'container': container_id})
{
'Container': container_id,
'EndpointConfig': {
'Aliases': ['foo', 'bar'],
},
})

@base.requires_api_version('1.21')
def test_disconnect_container_from_network(self):
Expand Down
10 changes: 9 additions & 1 deletion tests/unit/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
parse_repository_tag, parse_host, convert_filters, kwargs_from_env,
create_host_config, Ulimit, LogConfig, parse_bytes, parse_env_file,
exclude_paths, convert_volume_binds, decode_json_header, tar,
split_command, create_ipam_config, create_ipam_pool
split_command, create_ipam_config, create_ipam_pool,
)
from docker.utils.utils import create_endpoint_config
from docker.utils.ports import build_port_bindings, split_port

from .. import base
Expand Down Expand Up @@ -69,6 +70,13 @@ def test_create_host_config_with_oom_kill_disable(self):
InvalidVersion, lambda: create_host_config(version='1.18.3',
oom_kill_disable=True))

def test_create_endpoint_config_with_aliases(self):
config = create_endpoint_config(version='1.22', aliases=['foo', 'bar'])
assert config == {'Aliases': ['foo', 'bar']}

with pytest.raises(InvalidVersion):
create_endpoint_config(version='1.21', aliases=['foo', 'bar'])


class UlimitTest(base.BaseTestCase):
def test_create_host_config_dict_ulimit(self):
Expand Down