summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--docker/constants.py2
-rw-r--r--docker/utils/utils.py60
-rw-r--r--docs/hostconfig.md4
-rw-r--r--docs/tmpfs.md33
-rw-r--r--mkdocs.yml1
-rw-r--r--tests/integration/api_test.py2
-rw-r--r--tests/integration/container_test.py16
-rw-r--r--tests/integration/network_test.py56
-rw-r--r--tests/unit/container_test.py58
-rw-r--r--tests/unit/utils_test.py31
11 files changed, 231 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index 22abf50..307215b 100644
--- a/Makefile
+++ b/Makefile
@@ -32,14 +32,14 @@ integration-test-py3: build-py3
integration-dind: build build-py3
docker rm -vf dpy-dind || :
- docker run -d --name dpy-dind --env="DOCKER_HOST=tcp://localhost:2375" --privileged dockerswarm/dind:1.9.0 docker -d -H tcp://0.0.0.0:2375
+ docker run -d --name dpy-dind --env="DOCKER_HOST=tcp://localhost:2375" --privileged dockerswarm/dind:1.10.3 docker daemon -H tcp://0.0.0.0:2375
docker run --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-py py.test tests/integration
docker run --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-py3 py.test tests/integration
docker rm -vf dpy-dind
integration-dind-ssl: build-dind-certs build build-py3
docker run -d --name dpy-dind-certs dpy-dind-certs
- docker run -d --env="DOCKER_HOST=tcp://localhost:2375" --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --volumes-from dpy-dind-certs --name dpy-dind-ssl -v /tmp --privileged dockerswarm/dind:1.9.0 docker daemon --tlsverify --tlscacert=/certs/ca.pem --tlscert=/certs/server-cert.pem --tlskey=/certs/server-key.pem -H tcp://0.0.0.0:2375
+ docker run -d --env="DOCKER_HOST=tcp://localhost:2375" --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --volumes-from dpy-dind-certs --name dpy-dind-ssl -v /tmp --privileged dockerswarm/dind:1.10.3 docker daemon --tlsverify --tlscacert=/certs/ca.pem --tlscert=/certs/server-cert.pem --tlskey=/certs/server-key.pem -H tcp://0.0.0.0:2375
docker run --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375" --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --link=dpy-dind-ssl:docker docker-py py.test tests/integration
docker run --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375" --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --link=dpy-dind-ssl:docker docker-py3 py.test tests/integration
docker rm -vf dpy-dind-ssl dpy-dind-certs
diff --git a/docker/constants.py b/docker/constants.py
index 0627ba0..6c381de 100644
--- a/docker/constants.py
+++ b/docker/constants.py
@@ -1,4 +1,4 @@
-DEFAULT_DOCKER_API_VERSION = '1.21'
+DEFAULT_DOCKER_API_VERSION = '1.22'
DEFAULT_TIMEOUT_SECONDS = 60
STREAM_HEADER_SIZE_BYTES = 8
CONTAINER_LIMITS_KEYS = [
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index d4393d5..ee1d1b0 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -337,6 +337,35 @@ def convert_volume_binds(binds):
return result
+def convert_tmpfs_mounts(tmpfs):
+ if isinstance(tmpfs, dict):
+ return tmpfs
+
+ if not isinstance(tmpfs, list):
+ raise ValueError(
+ 'Expected tmpfs value to be either a list or a dict, found: {}'
+ .format(type(tmpfs).__name__)
+ )
+
+ result = {}
+ for mount in tmpfs:
+ if isinstance(mount, six.string_types):
+ if ":" in mount:
+ name, options = mount.split(":", 1)
+ else:
+ name = mount
+ options = ""
+
+ else:
+ raise ValueError(
+ "Expected item in tmpfs list to be a string, found: {}"
+ .format(type(mount).__name__)
+ )
+
+ result[name] = options
+ return result
+
+
def parse_repository_tag(repo_name):
parts = repo_name.rsplit('@', 1)
if len(parts) == 2:
@@ -371,11 +400,12 @@ def parse_host(addr, platform=None, tls=False):
if addr == 'tcp://':
raise errors.DockerException(
- "Invalid bind address format: {0}".format(addr))
+ "Invalid bind address format: {0}".format(addr)
+ )
elif addr.startswith('unix://'):
addr = addr[7:]
elif addr.startswith('tcp://'):
- proto = "http"
+ proto = 'http{0}'.format('s' if tls else '')
addr = addr[6:]
elif addr.startswith('https://'):
proto = "https"
@@ -449,15 +479,17 @@ def parse_devices(devices):
return device_list
-def kwargs_from_env(ssl_version=None, assert_hostname=None):
- host = os.environ.get('DOCKER_HOST')
+def kwargs_from_env(ssl_version=None, assert_hostname=None, environment=None):
+ if not environment:
+ environment = os.environ
+ host = environment.get('DOCKER_HOST')
# empty string for cert path is the same as unset.
- cert_path = os.environ.get('DOCKER_CERT_PATH') or None
+ cert_path = environment.get('DOCKER_CERT_PATH') or None
# empty string for tls verify counts as "false".
# Any value or 'unset' counts as true.
- tls_verify = os.environ.get('DOCKER_TLS_VERIFY')
+ tls_verify = environment.get('DOCKER_TLS_VERIFY')
if tls_verify == '':
tls_verify = False
else:
@@ -584,7 +616,7 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
mem_limit=None, memswap_limit=None, mem_swappiness=None,
cgroup_parent=None, group_add=None, cpu_quota=None,
cpu_period=None, oom_kill_disable=False, shm_size=None,
- version=None):
+ version=None, tmpfs=None, oom_score_adj=None):
host_config = {}
@@ -634,6 +666,15 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
host_config['OomKillDisable'] = oom_kill_disable
+ if oom_score_adj:
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('oom_score_adj', '1.22')
+ if not isinstance(oom_score_adj, int):
+ raise host_config_type_error(
+ 'oom_score_adj', oom_score_adj, 'int'
+ )
+ host_config['OomScoreAdj'] = oom_score_adj
+
if publish_all_ports:
host_config['PublishAllPorts'] = publish_all_ports
@@ -751,6 +792,11 @@ def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
host_config['CpuPeriod'] = cpu_period
+ if tmpfs:
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('tmpfs', '1.22')
+ host_config["Tmpfs"] = convert_tmpfs_mounts(tmpfs)
+
return host_config
diff --git a/docs/hostconfig.md b/docs/hostconfig.md
index 4b841d5..cd96433 100644
--- a/docs/hostconfig.md
+++ b/docs/hostconfig.md
@@ -73,6 +73,8 @@ for example:
for more information.
* lxc_conf (dict): LXC config
* oom_kill_disable (bool): Whether to disable OOM killer
+* oom_score_adj (int): An integer value containing the score given to the
+ container in order to tune OOM killer preferences
* publish_all_ports (bool): Whether to publish all ports to the host
* links (dict or list of tuples): either as a dictionary mapping name to alias
or as a list of `(name, alias)` tuples
@@ -111,6 +113,8 @@ for example:
container process will run as.
* devices (list): Host device bindings. See [host devices](host-devices.md)
for more information.
+* tmpfs: Temporary filesystems to mouunt. See [Using tmpfs](tmpfs.md) for more
+ information.
**Returns** (dict) HostConfig dictionary
diff --git a/docs/tmpfs.md b/docs/tmpfs.md
new file mode 100644
index 0000000..d8be9b6
--- /dev/null
+++ b/docs/tmpfs.md
@@ -0,0 +1,33 @@
+# Using tmpfs
+
+When creating a container, you can specify paths to be mounted with tmpfs using
+the `tmpfs` argument to `create_host_config`, similarly to the `--tmpfs`
+argument to `docker run`.
+
+This capability is supported in Docker Engine 1.10 and up.
+
+`tmpfs` can be either a list or a dictionary. If it's a list, each item is a
+string specifying the path and (optionally) any configuration for the mount:
+
+```python
+client.create_container(
+ 'busybox', 'ls',
+ host_config=client.create_host_config(tmpfs=[
+ '/mnt/vol2',
+ '/mnt/vol1:size=3G,uid=1000'
+ ])
+)
+```
+
+Alternatively, if it's a dictionary, each key is a path and each value contains
+the mount options:
+
+```python
+client.create_container(
+ 'busybox', 'ls',
+ host_config=client.create_host_config(tmpfs={
+ '/mnt/vol2': '',
+ '/mnt/vol1': 'size=3G,uid=1000'
+ })
+)
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index c6bfae1..64e9e6c 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -13,6 +13,7 @@ pages:
- Host devices: host-devices.md
- Host configuration: hostconfig.md
- Network configuration: networks.md
+- Using tmpfs: tmpfs.md
- Using with boot2docker: boot2docker.md
- Change Log: change_log.md
- Contributing: contributing.md
diff --git a/tests/integration/api_test.py b/tests/integration/api_test.py
index e120c84..67ed068 100644
--- a/tests/integration/api_test.py
+++ b/tests/integration/api_test.py
@@ -49,7 +49,7 @@ class LinkTest(helpers.BaseTestCase):
container2 = self.client.create_container(
helpers.BUSYBOX, 'cat', host_config=self.client.create_host_config(
- links={link_path: link_alias}, network_mode='none'
+ links={link_path: link_alias}
)
)
container2_id = container2['Id']
diff --git a/tests/integration/container_test.py b/tests/integration/container_test.py
index a1498f9..940e5b8 100644
--- a/tests/integration/container_test.py
+++ b/tests/integration/container_test.py
@@ -382,6 +382,22 @@ class CreateContainerTest(helpers.BaseTestCase):
sorted(['Foo', 'Other=one', 'Blank='])
)
+ @requires_api_version('1.22')
+ def test_create_with_tmpfs(self):
+ tmpfs = {
+ '/tmp1': 'size=3M'
+ }
+
+ container = self.client.create_container(
+ BUSYBOX,
+ ['echo'],
+ host_config=self.client.create_host_config(
+ tmpfs=tmpfs))
+
+ self.tmp_containers.append(container['Id'])
+ config = self.client.inspect_container(container)
+ assert config['HostConfig']['Tmpfs'] == tmpfs
+
class VolumeBindTest(helpers.BaseTestCase):
def setUp(self):
diff --git a/tests/integration/network_test.py b/tests/integration/network_test.py
index a744617..053e4ae 100644
--- a/tests/integration/network_test.py
+++ b/tests/integration/network_test.py
@@ -238,48 +238,56 @@ class TestNetworks(helpers.BaseTestCase):
@requires_api_version('1.22')
def test_connect_with_ipv4_address(self):
- net_name, net_id = self.create_network()
+ net_name, net_id = self.create_network(
+ ipam=create_ipam_config(
+ driver='default',
+ pool_configs=[
+ create_ipam_pool(
+ subnet="172.28.0.0/16", iprange="172.28.5.0/24",
+ gateway="172.28.5.254"
+ )
+ ]
+ )
+ )
container = self.create_and_start(
host_config=self.client.create_host_config(network_mode=net_name))
self.client.disconnect_container_from_network(container, net_name)
self.client.connect_container_to_network(
- container, net_name,
- ipv4_address='192.168.0.1')
+ container, net_name, ipv4_address='172.28.5.24'
+ )
container_data = self.client.inspect_container(container)
+ net_data = container_data['NetworkSettings']['Networks'][net_name]
self.assertEqual(
- container_data['NetworkSettings']['Networks'][net_name]
- ['IPAMConfig']['IPv4Address'],
- '192.168.0.1')
-
- self.create_and_start(
- name='docker-py-test-upstream',
- host_config=self.client.create_host_config(network_mode=net_name))
-
- self.execute(container, ['nslookup', 'bar'])
+ net_data['IPAMConfig']['IPv4Address'], '172.28.5.24'
+ )
@requires_api_version('1.22')
def test_connect_with_ipv6_address(self):
- net_name, net_id = self.create_network()
+ net_name, net_id = self.create_network(
+ ipam=create_ipam_config(
+ driver='default',
+ pool_configs=[
+ create_ipam_pool(
+ subnet="2001:389::1/64", iprange="2001:389::0/96",
+ gateway="2001:389::ffff"
+ )
+ ]
+ )
+ )
container = self.create_and_start(
host_config=self.client.create_host_config(network_mode=net_name))
self.client.disconnect_container_from_network(container, net_name)
self.client.connect_container_to_network(
- container, net_name,
- ipv6_address='2001:389::1')
+ container, net_name, ipv6_address='2001:389::f00d'
+ )
container_data = self.client.inspect_container(container)
+ net_data = container_data['NetworkSettings']['Networks'][net_name]
self.assertEqual(
- container_data['NetworkSettings']['Networks'][net_name]
- ['IPAMConfig']['IPv6Address'],
- '2001:389::1')
-
- self.create_and_start(
- name='docker-py-test-upstream',
- host_config=self.client.create_host_config(network_mode=net_name))
-
- self.execute(container, ['nslookup', 'bar'])
+ net_data['IPAMConfig']['IPv6Address'], '2001:389::f00d'
+ )
diff --git a/tests/unit/container_test.py b/tests/unit/container_test.py
index 6e23f89..2a72c17 100644
--- a/tests/unit/container_test.py
+++ b/tests/unit/container_test.py
@@ -1016,6 +1016,64 @@ class CreateContainerTest(DockerClientTest):
}
}}'''))
+ @requires_api_version('1.22')
+ def test_create_container_with_tmpfs_list(self):
+
+ self.client.create_container(
+ 'busybox', 'true', host_config=self.client.create_host_config(
+ tmpfs=[
+ "/tmp",
+ "/mnt:size=3G,uid=100"
+ ]
+ )
+ )
+
+ args = fake_request.call_args
+ self.assertEqual(args[0][1], url_prefix +
+ 'containers/create')
+ expected_payload = self.base_create_payload()
+ expected_payload['HostConfig'] = self.client.create_host_config()
+ expected_payload['HostConfig']['Tmpfs'] = {
+ "/tmp": "",
+ "/mnt": "size=3G,uid=100"
+ }
+ self.assertEqual(json.loads(args[1]['data']), expected_payload)
+ self.assertEqual(args[1]['headers'],
+ {'Content-Type': 'application/json'})
+ self.assertEqual(
+ args[1]['timeout'],
+ DEFAULT_TIMEOUT_SECONDS
+ )
+
+ @requires_api_version('1.22')
+ def test_create_container_with_tmpfs_dict(self):
+
+ self.client.create_container(
+ 'busybox', 'true', host_config=self.client.create_host_config(
+ tmpfs={
+ "/tmp": "",
+ "/mnt": "size=3G,uid=100"
+ }
+ )
+ )
+
+ args = fake_request.call_args
+ self.assertEqual(args[0][1], url_prefix +
+ 'containers/create')
+ expected_payload = self.base_create_payload()
+ expected_payload['HostConfig'] = self.client.create_host_config()
+ expected_payload['HostConfig']['Tmpfs'] = {
+ "/tmp": "",
+ "/mnt": "size=3G,uid=100"
+ }
+ self.assertEqual(json.loads(args[1]['data']), expected_payload)
+ self.assertEqual(args[1]['headers'],
+ {'Content-Type': 'application/json'})
+ self.assertEqual(
+ args[1]['timeout'],
+ DEFAULT_TIMEOUT_SECONDS
+ )
+
class ContainerTest(DockerClientTest):
def test_list_containers(self):
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
index 65b7cf8..eb952b2 100644
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -87,6 +87,16 @@ class HostConfigTest(base.BaseTestCase):
InvalidVersion, lambda: create_host_config(version='1.18.3',
oom_kill_disable=True))
+ def test_create_host_config_with_oom_score_adj(self):
+ config = create_host_config(version='1.22', oom_score_adj=100)
+ self.assertEqual(config.get('OomScoreAdj'), 100)
+ self.assertRaises(
+ InvalidVersion, lambda: create_host_config(version='1.21',
+ oom_score_adj=100))
+ self.assertRaises(
+ TypeError, lambda: create_host_config(version='1.22',
+ oom_score_adj='100'))
+
def test_create_endpoint_config_with_aliases(self):
config = create_endpoint_config(version='1.22', aliases=['foo', 'bar'])
assert config == {'Aliases': ['foo', 'bar']}
@@ -249,6 +259,20 @@ class KwargsFromEnvTest(base.BaseTestCase):
if temp_dir:
shutil.rmtree(temp_dir)
+ def test_kwargs_from_env_alternate_env(self):
+ # Values in os.environ are entirely ignored if an alternate is
+ # provided
+ os.environ.update(
+ DOCKER_HOST='tcp://192.168.59.103:2376',
+ DOCKER_CERT_PATH=TEST_CERT_DIR,
+ DOCKER_TLS_VERIFY=''
+ )
+ kwargs = kwargs_from_env(environment={
+ 'DOCKER_HOST': 'http://docker.gensokyo.jp:2581',
+ })
+ assert 'http://docker.gensokyo.jp:2581' == kwargs['base_url']
+ assert 'tls' not in kwargs
+
class ConverVolumeBindsTest(base.BaseTestCase):
def test_convert_volume_binds_empty(self):
@@ -412,7 +436,12 @@ class ParseHostTest(base.BaseTestCase):
def test_parse_host_tls(self):
host_value = 'myhost.docker.net:3348'
expected_result = 'https://myhost.docker.net:3348'
- self.assertEqual(parse_host(host_value, None, True), expected_result)
+ assert parse_host(host_value, tls=True) == expected_result
+
+ def test_parse_host_tls_tcp_proto(self):
+ host_value = 'tcp://myhost.docker.net:3348'
+ expected_result = 'https://myhost.docker.net:3348'
+ assert parse_host(host_value, tls=True) == expected_result
class ParseRepositoryTagTest(base.BaseTestCase):