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
7 changes: 7 additions & 0 deletions docker/models/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ def run(self, image, command=None, stdout=True, stderr=False,
cap_add (list of str): Add kernel capabilities. For example,
``["SYS_ADMIN", "MKNOD"]``.
cap_drop (list of str): Drop kernel capabilities.
cpu_count (int): Number of usable CPUs (Windows only).
cpu_percent (int): Usable percentage of the available CPUs
(Windows only).
cpu_period (int): The length of a CPU period in microseconds.
cpu_quota (int): Microseconds of CPU time that the container can
get in a CPU period.
Expand Down Expand Up @@ -523,6 +526,7 @@ def run(self, image, command=None, stdout=True, stderr=False,
networks (:py:class:`list`): A list of network names to connect
this container to.
name (str): The name for this container.
nano_cpus (int): CPU quota in units of 10-9 CPUs.
network_disabled (bool): Disable networking.
network_mode (str): One of:

Expand Down Expand Up @@ -801,6 +805,8 @@ def prune(self, filters=None):
'cap_add',
'cap_drop',
'cgroup_parent',
'cpu_count',
'cpu_percent',
'cpu_period',
'cpu_quota',
'cpu_shares',
Expand All @@ -827,6 +833,7 @@ def prune(self, filters=None):
'mem_reservation',
'mem_swappiness',
'memswap_limit',
'nano_cpus',
'network_mode',
'oom_kill_disable',
'oom_score_adj',
Expand Down
27 changes: 26 additions & 1 deletion docker/types/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def __init__(self, version, binds=None, port_bindings=None,
tmpfs=None, oom_score_adj=None, dns_opt=None, cpu_shares=None,
cpuset_cpus=None, userns_mode=None, pids_limit=None,
isolation=None, auto_remove=False, storage_opt=None,
init=None, init_path=None, volume_driver=None):
init=None, init_path=None, volume_driver=None,
cpu_count=None, cpu_percent=None, nano_cpus=None):

if mem_limit is not None:
self['Memory'] = parse_bytes(mem_limit)
Expand Down Expand Up @@ -433,6 +434,30 @@ def __init__(self, version, binds=None, port_bindings=None,
raise host_config_version_error('volume_driver', '1.21')
self['VolumeDriver'] = volume_driver

if cpu_count:
if not isinstance(cpu_count, int):
raise host_config_type_error('cpu_count', cpu_count, 'int')
if version_lt(version, '1.25'):
raise host_config_version_error('cpu_count', '1.25')

self['CpuCount'] = cpu_count

if cpu_percent:
if not isinstance(cpu_percent, int):
raise host_config_type_error('cpu_percent', cpu_percent, 'int')
if version_lt(version, '1.25'):
raise host_config_version_error('cpu_percent', '1.25')

self['CpuPercent'] = cpu_percent

if nano_cpus:
if not isinstance(nano_cpus, int):
raise host_config_type_error('nano_cpus', nano_cpus, 'int')
if version_lt(version, '1.25'):
raise host_config_version_error('nano_cpus', '1.25')

self['NanoCpus'] = nano_cpus


def host_config_type_error(param, param_value, expected):
error_msg = 'Invalid type for {0} param: expected {1} but found {2}'
Expand Down
32 changes: 32 additions & 0 deletions tests/unit/api_container_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,38 @@ def test_create_container_with_unicode_envvars(self):
self.assertEqual(args[0][1], url_prefix + 'containers/create')
self.assertEqual(json.loads(args[1]['data'])['Env'], expected)

@requires_api_version('1.25')
def test_create_container_with_host_config_cpus(self):
self.client.create_container(
'busybox', 'ls', host_config=self.client.create_host_config(
cpu_count=1,
cpu_percent=20,
nano_cpus=1000
)
)

args = fake_request.call_args
self.assertEqual(args[0][1],
url_prefix + 'containers/create')

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": {
"CpuCount": 1,
"CpuPercent": 20,
"NanoCpus": 1000,
"NetworkMode": "default"
}}'''))
self.assertEqual(
args[1]['headers'], {'Content-Type': 'application/json'}
)


class ContainerTest(BaseAPIClientTest):
def test_list_containers(self):
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/dockertypes_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,39 @@ def test_create_host_config_with_volume_driver(self):
config = create_host_config(version='1.21', volume_driver='local')
assert config.get('VolumeDriver') == 'local'

def test_create_host_config_invalid_cpu_count_types(self):
with pytest.raises(TypeError):
create_host_config(version='1.25', cpu_count='1')

def test_create_host_config_with_cpu_count(self):
config = create_host_config(version='1.25', cpu_count=2)
self.assertEqual(config.get('CpuCount'), 2)
self.assertRaises(
InvalidVersion, lambda: create_host_config(
version='1.24', cpu_count=1))

def test_create_host_config_invalid_cpu_percent_types(self):
with pytest.raises(TypeError):
create_host_config(version='1.25', cpu_percent='1')

def test_create_host_config_with_cpu_percent(self):
config = create_host_config(version='1.25', cpu_percent=15)
self.assertEqual(config.get('CpuPercent'), 15)
self.assertRaises(
InvalidVersion, lambda: create_host_config(
version='1.24', cpu_percent=10))

def test_create_host_config_invalid_nano_cpus_types(self):
with pytest.raises(TypeError):
create_host_config(version='1.25', nano_cpus='0')

def test_create_host_config_with_nano_cpus(self):
config = create_host_config(version='1.25', nano_cpus=1000)
self.assertEqual(config.get('NanoCpus'), 1000)
self.assertRaises(
InvalidVersion, lambda: create_host_config(
version='1.24', nano_cpus=1))


class ContainerConfigTest(unittest.TestCase):
def test_create_container_config_volume_driver_warning(self):
Expand Down