Replace `network_config` with a dict of EndpointConfig

- Renamed parameter from `network_config` to `networking_config` to be more semantically correct with the rest of the API.
This commit is contained in:
Mariano Scazzariello 2023-09-30 00:20:44 +02:00
parent 0318ad8e7e
commit 7752996f78
No known key found for this signature in database
GPG Key ID: 95C376C0221294BF
3 changed files with 159 additions and 107 deletions

View File

@ -2,16 +2,16 @@ import copy
import ntpath import ntpath
from collections import namedtuple from collections import namedtuple
from .images import Image
from .resource import Collection, Model
from ..api import APIClient from ..api import APIClient
from ..constants import DEFAULT_DATA_CHUNK_SIZE from ..constants import DEFAULT_DATA_CHUNK_SIZE
from ..errors import ( from ..errors import (
ContainerError, DockerException, ImageNotFound, ContainerError, DockerException, ImageNotFound,
NotFound, create_unexpected_kwargs_error NotFound, create_unexpected_kwargs_error
) )
from ..types import EndpointConfig, HostConfig, NetworkingConfig from ..types import HostConfig, NetworkingConfig
from ..utils import version_gte from ..utils import version_gte
from .images import Image
from .resource import Collection, Model
class Container(Model): class Container(Model):
@ -21,6 +21,7 @@ class Container(Model):
query the Docker daemon for the current properties, causing query the Docker daemon for the current properties, causing
:py:attr:`attrs` to be refreshed. :py:attr:`attrs` to be refreshed.
""" """
@property @property
def name(self): def name(self):
""" """
@ -680,33 +681,13 @@ class ContainerCollection(Collection):
This mode is incompatible with ``ports``. This mode is incompatible with ``ports``.
Incompatible with ``network``. Incompatible with ``network``.
network_config (dict): A dictionary containing options that are networking_config (Dict[str, EndpointConfig]):
passed to the network driver during the connection. Dictionary of EndpointConfig objects for each container network.
The key is the name of the network.
Defaults to ``None``. Defaults to ``None``.
The dictionary contains the following keys:
- ``aliases`` (:py:class:`list`): A list of aliases for
the network endpoint.
Names in that list can be used within the network to
reach this container. Defaults to ``None``.
- ``links`` (:py:class:`list`): A list of links for
the network endpoint endpoint.
Containers declared in this list will be linked to this
container. Defaults to ``None``.
- ``ipv4_address`` (str): The IP address to assign to
this container on the network, using the IPv4 protocol.
Defaults to ``None``.
- ``ipv6_address`` (str): The IP address to assign to
this container on the network, using the IPv6 protocol.
Defaults to ``None``.
- ``link_local_ips`` (:py:class:`list`): A list of link-local
(IPv4/IPv6) addresses.
- ``driver_opt`` (dict): A dictionary of options to provide to
the network driver. Defaults to ``None``.
- ``mac_address`` (str): MAC Address to assign to the network
interface. Defaults to ``None``. Requires API >= 1.25.
Used in conjuction with ``network``. Used in conjuction with ``network``.
Incompatible with ``network_mode``. Incompatible with ``network_mode``.
oom_kill_disable (bool): Whether to disable OOM killer. oom_kill_disable (bool): Whether to disable OOM killer.
oom_score_adj (int): An integer value containing the score given oom_score_adj (int): An integer value containing the score given
@ -872,9 +853,9 @@ class ContainerCollection(Collection):
'together.' 'together.'
) )
if kwargs.get('network_config') and not kwargs.get('network'): if kwargs.get('networking_config') and not kwargs.get('network'):
raise RuntimeError( raise RuntimeError(
'The option "network_config" can not be used ' 'The option "networking_config" can not be used '
'without "network".' 'without "network".'
) )
@ -1030,6 +1011,7 @@ class ContainerCollection(Collection):
def prune(self, filters=None): def prune(self, filters=None):
return self.client.api.prune_containers(filters=filters) return self.client.api.prune_containers(filters=filters)
prune.__doc__ = APIClient.prune_containers.__doc__ prune.__doc__ = APIClient.prune_containers.__doc__
@ -1124,17 +1106,6 @@ RUN_HOST_CONFIG_KWARGS = [
] ]
NETWORKING_CONFIG_ARGS = [
'aliases',
'links',
'ipv4_address',
'ipv6_address',
'link_local_ips',
'driver_opt',
'mac_address'
]
def _create_container_args(kwargs): def _create_container_args(kwargs):
""" """
Convert arguments to create() to arguments to create_container(). Convert arguments to create() to arguments to create_container().
@ -1159,24 +1130,17 @@ def _create_container_args(kwargs):
host_config_kwargs['binds'] = volumes host_config_kwargs['binds'] = volumes
network = kwargs.pop('network', None) network = kwargs.pop('network', None)
network_config = kwargs.pop('network_config', None) networking_config = kwargs.pop('networking_config', None)
if network: if network:
endpoint_config = None if networking_config:
# Sanity check: check if the network is defined in the
if network_config: # networking config dict, otherwise switch to None
clean_endpoint_args = {} if network not in networking_config:
for arg_name in NETWORKING_CONFIG_ARGS: networking_config = None
if arg_name in network_config:
clean_endpoint_args[arg_name] = network_config[arg_name]
if clean_endpoint_args:
endpoint_config = EndpointConfig(
host_config_kwargs['version'], **clean_endpoint_args
)
create_kwargs['networking_config'] = NetworkingConfig( create_kwargs['networking_config'] = NetworkingConfig(
{network: endpoint_config} networking_config
) if endpoint_config else {network: None} ) if networking_config else {network: None}
host_config_kwargs['network_mode'] = network host_config_kwargs['network_mode'] = network
# All kwargs should have been consumed by this point, so raise # All kwargs should have been consumed by this point, so raise

View File

@ -5,10 +5,10 @@ import threading
import pytest import pytest
import docker import docker
from ..helpers import random_name
from ..helpers import requires_api_version
from .base import BaseIntegrationTest from .base import BaseIntegrationTest
from .base import TEST_API_VERSION from .base import TEST_API_VERSION
from ..helpers import random_name
from ..helpers import requires_api_version
class ContainerCollectionTest(BaseIntegrationTest): class ContainerCollectionTest(BaseIntegrationTest):
@ -104,7 +104,7 @@ class ContainerCollectionTest(BaseIntegrationTest):
assert 'Networks' in attrs['NetworkSettings'] assert 'Networks' in attrs['NetworkSettings']
assert list(attrs['NetworkSettings']['Networks'].keys()) == [net_name] assert list(attrs['NetworkSettings']['Networks'].keys()) == [net_name]
def test_run_with_network_config(self): def test_run_with_networking_config(self):
net_name = random_name() net_name = random_name()
client = docker.from_env(version=TEST_API_VERSION) client = docker.from_env(version=TEST_API_VERSION)
client.networks.create(net_name) client.networks.create(net_name)
@ -113,10 +113,16 @@ class ContainerCollectionTest(BaseIntegrationTest):
test_aliases = ['hello'] test_aliases = ['hello']
test_driver_opt = {'key1': 'a'} test_driver_opt = {'key1': 'a'}
networking_config = {
net_name: client.api.create_endpoint_config(
aliases=test_aliases,
driver_opt=test_driver_opt
)
}
container = client.containers.run( container = client.containers.run(
'alpine', 'echo hello world', network=net_name, 'alpine', 'echo hello world', network=net_name,
network_config={'aliases': test_aliases, networking_config=networking_config,
'driver_opt': test_driver_opt},
detach=True detach=True
) )
self.tmp_containers.append(container.id) self.tmp_containers.append(container.id)
@ -131,7 +137,7 @@ class ContainerCollectionTest(BaseIntegrationTest):
assert attrs['NetworkSettings']['Networks'][net_name]['DriverOpts'] \ assert attrs['NetworkSettings']['Networks'][net_name]['DriverOpts'] \
== test_driver_opt == test_driver_opt
def test_run_with_network_config_undeclared_params(self): def test_run_with_networking_config_with_undeclared_network(self):
net_name = random_name() net_name = random_name()
client = docker.from_env(version=TEST_API_VERSION) client = docker.from_env(version=TEST_API_VERSION)
client.networks.create(net_name) client.networks.create(net_name)
@ -140,11 +146,41 @@ class ContainerCollectionTest(BaseIntegrationTest):
test_aliases = ['hello'] test_aliases = ['hello']
test_driver_opt = {'key1': 'a'} test_driver_opt = {'key1': 'a'}
networking_config = {
net_name: client.api.create_endpoint_config(
aliases=test_aliases,
driver_opt=test_driver_opt
),
'bar': client.api.create_endpoint_config(
aliases=['test'],
driver_opt={'key2': 'b'}
),
}
with pytest.raises(docker.errors.APIError) as e:
container = client.containers.run( container = client.containers.run(
'alpine', 'echo hello world', network=net_name, 'alpine', 'echo hello world', network=net_name,
network_config={'aliases': test_aliases, networking_config=networking_config,
'driver_opt': test_driver_opt, detach=True
'undeclared_param': 'random_value'}, )
self.tmp_containers.append(container.id)
def test_run_with_networking_config_only_undeclared_network(self):
net_name = random_name()
client = docker.from_env(version=TEST_API_VERSION)
client.networks.create(net_name)
self.tmp_networks.append(net_name)
networking_config = {
'bar': client.api.create_endpoint_config(
aliases=['hello'],
driver_opt={'key1': 'a'}
),
}
container = client.containers.run(
'alpine', 'echo hello world', network=net_name,
networking_config=networking_config,
detach=True detach=True
) )
self.tmp_containers.append(container.id) self.tmp_containers.append(container.id)
@ -154,12 +190,9 @@ class ContainerCollectionTest(BaseIntegrationTest):
assert 'NetworkSettings' in attrs assert 'NetworkSettings' in attrs
assert 'Networks' in attrs['NetworkSettings'] assert 'Networks' in attrs['NetworkSettings']
assert list(attrs['NetworkSettings']['Networks'].keys()) == [net_name] assert list(attrs['NetworkSettings']['Networks'].keys()) == [net_name]
assert attrs['NetworkSettings']['Networks'][net_name]['Aliases'] == \ assert attrs['NetworkSettings']['Networks'][net_name]['Aliases'] is None
test_aliases assert (attrs['NetworkSettings']['Networks'][net_name]['DriverOpts']
assert attrs['NetworkSettings']['Networks'][net_name]['DriverOpts'] \ is None)
== test_driver_opt
assert 'undeclared_param' not in \
attrs['NetworkSettings']['Networks'][net_name]
def test_run_with_none_driver(self): def test_run_with_none_driver(self):
client = docker.from_env(version=TEST_API_VERSION) client = docker.from_env(version=TEST_API_VERSION)

View File

@ -1,11 +1,13 @@
import pytest
import unittest import unittest
import pytest
import docker import docker
from docker.constants import DEFAULT_DATA_CHUNK_SIZE, \ from docker.constants import DEFAULT_DATA_CHUNK_SIZE, \
DEFAULT_DOCKER_API_VERSION DEFAULT_DOCKER_API_VERSION
from docker.models.containers import Container, _create_container_args from docker.models.containers import Container, _create_container_args
from docker.models.images import Image from docker.models.images import Image
from docker.types import EndpointConfig
from .fake_api import FAKE_CONTAINER_ID, FAKE_IMAGE_ID, FAKE_EXEC_ID from .fake_api import FAKE_CONTAINER_ID, FAKE_IMAGE_ID, FAKE_EXEC_ID
from .fake_api_client import make_fake_client from .fake_api_client import make_fake_client
@ -32,6 +34,13 @@ class ContainerCollectionTest(unittest.TestCase):
) )
def test_create_container_args(self): def test_create_container_args(self):
networking_config = {
'foo': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test'],
driver_opt={'key1': 'a'}
)
}
create_kwargs = _create_container_args(dict( create_kwargs = _create_container_args(dict(
image='alpine', image='alpine',
command='echo hello world', command='echo hello world',
@ -75,7 +84,7 @@ class ContainerCollectionTest(unittest.TestCase):
name='somename', name='somename',
network_disabled=False, network_disabled=False,
network='foo', network='foo',
network_config={'aliases': ['test'], 'driver_opt': {'key1': 'a'}}, networking_config=networking_config,
oom_kill_disable=True, oom_kill_disable=True,
oom_score_adj=5, oom_score_adj=5,
pid_mode='host', pid_mode='host',
@ -349,35 +358,41 @@ class ContainerCollectionTest(unittest.TestCase):
host_config={'NetworkMode': 'default'}, host_config={'NetworkMode': 'default'},
) )
def test_run_network_config_without_network(self): def test_run_networking_config_without_network(self):
client = make_fake_client() client = make_fake_client()
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
client.containers.run( client.containers.run(
image='alpine', image='alpine',
network_config={'aliases': ['test'], networking_config={'aliases': ['test'],
'driver_opt': {'key1': 'a'}} 'driver_opt': {'key1': 'a'}}
) )
def test_run_network_config_with_network_mode(self): def test_run_networking_config_with_network_mode(self):
client = make_fake_client() client = make_fake_client()
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
client.containers.run( client.containers.run(
image='alpine', image='alpine',
network_mode='none', network_mode='none',
network_config={'aliases': ['test'], networking_config={'aliases': ['test'],
'driver_opt': {'key1': 'a'}} 'driver_opt': {'key1': 'a'}}
) )
def test_run_network_config(self): def test_run_networking_config(self):
client = make_fake_client() client = make_fake_client()
networking_config = {
'foo': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test'],
driver_opt={'key1': 'a'}
)
}
client.containers.run( client.containers.run(
image='alpine', image='alpine',
network='foo', network='foo',
network_config={'aliases': ['test'], networking_config=networking_config
'driver_opt': {'key1': 'a'}}
) )
client.api.create_container.assert_called_with( client.api.create_container.assert_called_with(
@ -390,15 +405,24 @@ class ContainerCollectionTest(unittest.TestCase):
host_config={'NetworkMode': 'foo'} host_config={'NetworkMode': 'foo'}
) )
def test_run_network_config_undeclared_params(self): def test_run_networking_config_with_undeclared_network(self):
client = make_fake_client() client = make_fake_client()
networking_config = {
'foo': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test_foo'],
driver_opt={'key2': 'b'}
),
'bar': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test'],
driver_opt={'key1': 'a'}
)
}
client.containers.run( client.containers.run(
image='alpine', image='alpine',
network='foo', network='foo',
network_config={'aliases': ['test'], networking_config=networking_config
'driver_opt': {'key1': 'a'},
'undeclared_param': 'random_value'}
) )
client.api.create_container.assert_called_with( client.api.create_container.assert_called_with(
@ -406,18 +430,26 @@ class ContainerCollectionTest(unittest.TestCase):
image='alpine', image='alpine',
command=None, command=None,
networking_config={'EndpointsConfig': { networking_config={'EndpointsConfig': {
'foo': {'Aliases': ['test'], 'DriverOpts': {'key1': 'a'}}} 'foo': {'Aliases': ['test_foo'], 'DriverOpts': {'key2': 'b'}},
}, 'bar': {'Aliases': ['test'], 'DriverOpts': {'key1': 'a'}},
}},
host_config={'NetworkMode': 'foo'} host_config={'NetworkMode': 'foo'}
) )
def test_run_network_config_only_undeclared_params(self): def test_run_networking_config_only_undeclared_network(self):
client = make_fake_client() client = make_fake_client()
networking_config = {
'bar': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test'],
driver_opt={'key1': 'a'}
)
}
client.containers.run( client.containers.run(
image='alpine', image='alpine',
network='foo', network='foo',
network_config={'undeclared_param': 'random_value'} networking_config=networking_config
) )
client.api.create_container.assert_called_with( client.api.create_container.assert_called_with(
@ -455,12 +487,12 @@ class ContainerCollectionTest(unittest.TestCase):
host_config={'NetworkMode': 'default'} host_config={'NetworkMode': 'default'}
) )
def test_create_network_config_without_network(self): def test_create_networking_config_without_network(self):
client = make_fake_client() client = make_fake_client()
client.containers.create( client.containers.create(
image='alpine', image='alpine',
network_config={'aliases': ['test'], networking_config={'aliases': ['test'],
'driver_opt': {'key1': 'a'}} 'driver_opt': {'key1': 'a'}}
) )
@ -470,13 +502,13 @@ class ContainerCollectionTest(unittest.TestCase):
host_config={'NetworkMode': 'default'} host_config={'NetworkMode': 'default'}
) )
def test_create_network_config_with_network_mode(self): def test_create_networking_config_with_network_mode(self):
client = make_fake_client() client = make_fake_client()
client.containers.create( client.containers.create(
image='alpine', image='alpine',
network_mode='none', network_mode='none',
network_config={'aliases': ['test'], networking_config={'aliases': ['test'],
'driver_opt': {'key1': 'a'}} 'driver_opt': {'key1': 'a'}}
) )
@ -486,14 +518,20 @@ class ContainerCollectionTest(unittest.TestCase):
host_config={'NetworkMode': 'none'} host_config={'NetworkMode': 'none'}
) )
def test_create_network_config(self): def test_create_networking_config(self):
client = make_fake_client() client = make_fake_client()
networking_config = {
'foo': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test'],
driver_opt={'key1': 'a'}
)
}
client.containers.create( client.containers.create(
image='alpine', image='alpine',
network='foo', network='foo',
network_config={'aliases': ['test'], networking_config=networking_config
'driver_opt': {'key1': 'a'}}
) )
client.api.create_container.assert_called_with( client.api.create_container.assert_called_with(
@ -505,33 +543,50 @@ class ContainerCollectionTest(unittest.TestCase):
host_config={'NetworkMode': 'foo'} host_config={'NetworkMode': 'foo'}
) )
def test_create_network_config_undeclared_params(self): def test_create_networking_config_with_undeclared_network(self):
client = make_fake_client() client = make_fake_client()
networking_config = {
'foo': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test_foo'],
driver_opt={'key2': 'b'}
),
'bar': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test'],
driver_opt={'key1': 'a'}
)
}
client.containers.create( client.containers.create(
image='alpine', image='alpine',
network='foo', network='foo',
network_config={'aliases': ['test'], networking_config=networking_config
'driver_opt': {'key1': 'a'},
'undeclared_param': 'random_value'}
) )
client.api.create_container.assert_called_with( client.api.create_container.assert_called_with(
image='alpine', image='alpine',
command=None, command=None,
networking_config={'EndpointsConfig': { networking_config={'EndpointsConfig': {
'foo': {'Aliases': ['test'], 'DriverOpts': {'key1': 'a'}}} 'foo': {'Aliases': ['test_foo'], 'DriverOpts': {'key2': 'b'}},
}, 'bar': {'Aliases': ['test'], 'DriverOpts': {'key1': 'a'}},
}},
host_config={'NetworkMode': 'foo'} host_config={'NetworkMode': 'foo'}
) )
def test_create_network_config_only_undeclared_params(self): def test_create_networking_config_only_undeclared_network(self):
client = make_fake_client() client = make_fake_client()
networking_config = {
'bar': EndpointConfig(
DEFAULT_DOCKER_API_VERSION, aliases=['test'],
driver_opt={'key1': 'a'}
)
}
client.containers.create( client.containers.create(
image='alpine', image='alpine',
network='foo', network='foo',
network_config={'undeclared_param': 'random_value'} networking_config=networking_config
) )
client.api.create_container.assert_called_with( client.api.create_container.assert_called_with(