diff --git a/docker/utils/utils.py b/docker/utils/utils.py index d4393d584d..c5914cf657 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: @@ -584,7 +613,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): host_config = {} @@ -751,6 +780,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 4b841d51a4..19cd4ca208 100644 --- a/docs/hostconfig.md +++ b/docs/hostconfig.md @@ -111,6 +111,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 0000000000..d8be9b68c6 --- /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 c6bfae1013..64e9e6cd39 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/container_test.py b/tests/integration/container_test.py index a1498f9374..940e5b83ff 100644 --- a/tests/integration/container_test.py +++ b/tests/integration/container_test.py @@ -382,6 +382,22 @@ def test_create_with_environment_variable_no_value(self): 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/unit/container_test.py b/tests/unit/container_test.py index 6e23f89210..2a72c17936 100644 --- a/tests/unit/container_test.py +++ b/tests/unit/container_test.py @@ -1016,6 +1016,64 @@ def test_create_container_with_aliases(self): } }}''')) + @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):