From 461b600068465fb9260f3918ba12e8d501986515 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Tue, 31 Mar 2015 15:23:34 -0400 Subject: [PATCH 1/6] Merge pull request #1225 from aanand/fix-1222 When extending, `build` replaces `image` and vice versa (cherry picked from commit 6dbe321a45dfd7539234f889825b54e1a026e46f) Signed-off-by: Aanand Prasad --- compose/config.py | 6 ++++++ tests/unit/config_test.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/compose/config.py b/compose/config.py index 0cd7c1ae..8d6ffea7 100644 --- a/compose/config.py +++ b/compose/config.py @@ -189,6 +189,12 @@ def merge_service_dicts(base, override): override.get('volumes'), ) + if 'image' in override and 'build' in d: + del d['build'] + + if 'build' in override and 'image' in d: + del d['image'] + for k in ALLOWED_KEYS: if k not in ['environment', 'volumes']: if k in override: diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py index 8deb457a..67f24a92 100644 --- a/tests/unit/config_test.py +++ b/tests/unit/config_test.py @@ -79,6 +79,39 @@ class MergeTest(unittest.TestCase): ) self.assertEqual(set(service_dict['volumes']), set(['/bar:/code', '/data'])) + def test_merge_build_or_image_no_override(self): + self.assertEqual( + config.merge_service_dicts({'build': '.'}, {}), + {'build': '.'}, + ) + + self.assertEqual( + config.merge_service_dicts({'image': 'redis'}, {}), + {'image': 'redis'}, + ) + + def test_merge_build_or_image_override_with_same(self): + self.assertEqual( + config.merge_service_dicts({'build': '.'}, {'build': './web'}), + {'build': './web'}, + ) + + self.assertEqual( + config.merge_service_dicts({'image': 'redis'}, {'image': 'postgres'}), + {'image': 'postgres'}, + ) + + def test_merge_build_or_image_override_with_other(self): + self.assertEqual( + config.merge_service_dicts({'build': '.'}, {'image': 'redis'}), + {'image': 'redis'} + ) + + self.assertEqual( + config.merge_service_dicts({'image': 'redis'}, {'build': '.'}), + {'build': '.'} + ) + class EnvTest(unittest.TestCase): def test_parse_environment_as_list(self): From b24a60ba9fdbfc9d3bdade6efbc9b629cdd4872b Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Tue, 31 Mar 2015 16:01:22 -0400 Subject: [PATCH 2/6] Merge pull request #1226 from aanand/merge-multi-value-options Merge multi-value options when extending (cherry picked from commit e708f4f59dcb417e90a5bbdcadcee37e8c6b7802) Signed-off-by: Aanand Prasad --- compose/config.py | 30 ++++++++++++++--- tests/unit/config_test.py | 71 +++++++++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/compose/config.py b/compose/config.py index 8d6ffea7..7f2e302b 100644 --- a/compose/config.py +++ b/compose/config.py @@ -195,10 +195,23 @@ def merge_service_dicts(base, override): if 'build' in override and 'image' in d: del d['image'] - for k in ALLOWED_KEYS: - if k not in ['environment', 'volumes']: - if k in override: - d[k] = override[k] + list_keys = ['ports', 'expose', 'external_links'] + + for key in list_keys: + if key in base or key in override: + d[key] = base.get(key, []) + override.get(key, []) + + list_or_string_keys = ['dns', 'dns_search'] + + for key in list_or_string_keys: + if key in base or key in override: + d[key] = to_list(base.get(key)) + to_list(override.get(key)) + + already_merged_keys = ['environment', 'volumes'] + list_keys + list_or_string_keys + + for k in set(ALLOWED_KEYS) - set(already_merged_keys): + if k in override: + d[k] = override[k] return d @@ -354,6 +367,15 @@ def expand_path(working_dir, path): return os.path.abspath(os.path.join(working_dir, path)) +def to_list(value): + if value is None: + return [] + elif isinstance(value, six.string_types): + return [value] + else: + return value + + def get_service_name_from_net(net_config): if not net_config: return diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py index 67f24a92..d95ba783 100644 --- a/tests/unit/config_test.py +++ b/tests/unit/config_test.py @@ -39,40 +39,40 @@ class ConfigTest(unittest.TestCase): config.make_service_dict('foo', {'ports': ['8000']}) -class MergeTest(unittest.TestCase): - def test_merge_volumes_empty(self): +class MergeVolumesTest(unittest.TestCase): + def test_empty(self): service_dict = config.merge_service_dicts({}, {}) self.assertNotIn('volumes', service_dict) - def test_merge_volumes_no_override(self): + def test_no_override(self): service_dict = config.merge_service_dicts( {'volumes': ['/foo:/code', '/data']}, {}, ) self.assertEqual(set(service_dict['volumes']), set(['/foo:/code', '/data'])) - def test_merge_volumes_no_base(self): + def test_no_base(self): service_dict = config.merge_service_dicts( {}, {'volumes': ['/bar:/code']}, ) self.assertEqual(set(service_dict['volumes']), set(['/bar:/code'])) - def test_merge_volumes_override_explicit_path(self): + def test_override_explicit_path(self): service_dict = config.merge_service_dicts( {'volumes': ['/foo:/code', '/data']}, {'volumes': ['/bar:/code']}, ) self.assertEqual(set(service_dict['volumes']), set(['/bar:/code', '/data'])) - def test_merge_volumes_add_explicit_path(self): + def test_add_explicit_path(self): service_dict = config.merge_service_dicts( {'volumes': ['/foo:/code', '/data']}, {'volumes': ['/bar:/code', '/quux:/data']}, ) self.assertEqual(set(service_dict['volumes']), set(['/bar:/code', '/quux:/data'])) - def test_merge_volumes_remove_explicit_path(self): + def test_remove_explicit_path(self): service_dict = config.merge_service_dicts( {'volumes': ['/foo:/code', '/quux:/data']}, {'volumes': ['/bar:/code', '/data']}, @@ -113,6 +113,63 @@ class MergeTest(unittest.TestCase): ) +class MergeListsTest(unittest.TestCase): + def test_empty(self): + service_dict = config.merge_service_dicts({}, {}) + self.assertNotIn('ports', service_dict) + + def test_no_override(self): + service_dict = config.merge_service_dicts( + {'ports': ['10:8000', '9000']}, + {}, + ) + self.assertEqual(set(service_dict['ports']), set(['10:8000', '9000'])) + + def test_no_base(self): + service_dict = config.merge_service_dicts( + {}, + {'ports': ['10:8000', '9000']}, + ) + self.assertEqual(set(service_dict['ports']), set(['10:8000', '9000'])) + + def test_add_item(self): + service_dict = config.merge_service_dicts( + {'ports': ['10:8000', '9000']}, + {'ports': ['20:8000']}, + ) + self.assertEqual(set(service_dict['ports']), set(['10:8000', '9000', '20:8000'])) + + +class MergeStringsOrListsTest(unittest.TestCase): + def test_no_override(self): + service_dict = config.merge_service_dicts( + {'dns': '8.8.8.8'}, + {}, + ) + self.assertEqual(set(service_dict['dns']), set(['8.8.8.8'])) + + def test_no_base(self): + service_dict = config.merge_service_dicts( + {}, + {'dns': '8.8.8.8'}, + ) + self.assertEqual(set(service_dict['dns']), set(['8.8.8.8'])) + + def test_add_string(self): + service_dict = config.merge_service_dicts( + {'dns': ['8.8.8.8']}, + {'dns': '9.9.9.9'}, + ) + self.assertEqual(set(service_dict['dns']), set(['8.8.8.8', '9.9.9.9'])) + + def test_add_list(self): + service_dict = config.merge_service_dicts( + {'dns': '8.8.8.8'}, + {'dns': ['9.9.9.9']}, + ) + self.assertEqual(set(service_dict['dns']), set(['8.8.8.8', '9.9.9.9'])) + + class EnvTest(unittest.TestCase): def test_parse_environment_as_list(self): environment =[ From e4e802d1f86ceb16d45d0176f94906e799f90fc9 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Tue, 31 Mar 2015 21:20:02 -0400 Subject: [PATCH 3/6] Merge pull request #1213 from moysesb/relative_build Make value of 'build:' relative to the yml file. (cherry picked from commit 0f70b8638ff7167e9755d24dc8dab1579662f72d) Signed-off-by: Aanand Prasad --- compose/config.py | 14 ++++++++ docs/yml.md | 5 +-- tests/fixtures/build-ctx/Dockerfile | 2 ++ tests/fixtures/build-path/docker-compose.yml | 2 ++ .../docker-compose.yml | 2 +- .../simple-dockerfile/docker-compose.yml | 2 +- tests/unit/config_test.py | 33 +++++++++++++++++++ 7 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/build-ctx/Dockerfile create mode 100644 tests/fixtures/build-path/docker-compose.yml diff --git a/compose/config.py b/compose/config.py index 7f2e302b..1dc64af2 100644 --- a/compose/config.py +++ b/compose/config.py @@ -171,6 +171,9 @@ def process_container_options(service_dict, working_dir=None): if 'volumes' in service_dict: service_dict['volumes'] = resolve_host_paths(service_dict['volumes'], working_dir=working_dir) + if 'build' in service_dict: + service_dict['build'] = resolve_build_path(service_dict['build'], working_dir=working_dir) + return service_dict @@ -330,6 +333,17 @@ def resolve_host_path(volume, working_dir): return container_path +def resolve_build_path(build_path, working_dir=None): + if working_dir is None: + raise Exception("No working_dir passed to resolve_build_path") + + _path = expand_path(working_dir, build_path) + if not os.path.exists(_path) or not os.access(_path, os.R_OK): + raise ConfigurationError("build path %s either does not exist or is not accessible." % _path) + else: + return _path + + def merge_volumes(base, override): d = dict_from_volumes(base) d.update(dict_from_volumes(override)) diff --git a/docs/yml.md b/docs/yml.md index 157ba4e6..a9909e81 100644 --- a/docs/yml.md +++ b/docs/yml.md @@ -29,8 +29,9 @@ image: a4bc65fd ### build -Path to a directory containing a Dockerfile. This directory is also the -build context that is sent to the Docker daemon. +Path to a directory containing a Dockerfile. When the value supplied is a +relative path, it is interpreted as relative to the location of the yml file +itself. This directory is also the build context that is sent to the Docker daemon. Compose will build and tag it with a generated name, and use that image thereafter. diff --git a/tests/fixtures/build-ctx/Dockerfile b/tests/fixtures/build-ctx/Dockerfile new file mode 100644 index 00000000..d1ceac6b --- /dev/null +++ b/tests/fixtures/build-ctx/Dockerfile @@ -0,0 +1,2 @@ +FROM busybox:latest +CMD echo "success" diff --git a/tests/fixtures/build-path/docker-compose.yml b/tests/fixtures/build-path/docker-compose.yml new file mode 100644 index 00000000..66e8916e --- /dev/null +++ b/tests/fixtures/build-path/docker-compose.yml @@ -0,0 +1,2 @@ +foo: + build: ../build-ctx/ diff --git a/tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml b/tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml index a1038118..78631502 100644 --- a/tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml +++ b/tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml @@ -1,2 +1,2 @@ service: - build: tests/fixtures/dockerfile_with_entrypoint + build: . diff --git a/tests/fixtures/simple-dockerfile/docker-compose.yml b/tests/fixtures/simple-dockerfile/docker-compose.yml index a3f56d46..b0357541 100644 --- a/tests/fixtures/simple-dockerfile/docker-compose.yml +++ b/tests/fixtures/simple-dockerfile/docker-compose.yml @@ -1,2 +1,2 @@ simple: - build: tests/fixtures/simple-dockerfile + build: . diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py index d95ba783..f25f3a9d 100644 --- a/tests/unit/config_test.py +++ b/tests/unit/config_test.py @@ -381,3 +381,36 @@ class ExtendsTest(unittest.TestCase): ] self.assertEqual(set(dicts[0]['volumes']), set(paths)) + + +class BuildPathTest(unittest.TestCase): + def setUp(self): + self.abs_context_path = os.path.join(os.getcwd(), 'tests/fixtures/build-ctx') + + def test_nonexistent_path(self): + options = {'build': 'nonexistent.path'} + self.assertRaises( + config.ConfigurationError, + lambda: config.make_service_dict('foo', options, 'tests/fixtures/build-path'), + ) + + def test_relative_path(self): + relative_build_path = '../build-ctx/' + service_dict = config.make_service_dict( + 'relpath', + {'build': relative_build_path}, + working_dir='tests/fixtures/build-path' + ) + self.assertEquals(service_dict['build'], self.abs_context_path) + + def test_absolute_path(self): + service_dict = config.make_service_dict( + 'abspath', + {'build': self.abs_context_path}, + working_dir='tests/fixtures/build-path' + ) + self.assertEquals(service_dict['build'], self.abs_context_path) + + def test_from_file(self): + service_dict = config.load('tests/fixtures/build-path/docker-compose.yml') + self.assertEquals(service_dict, [{'name': 'foo', 'build': self.abs_context_path}]) From 78227c3c068a3ca7be47d3104fceb8c1e065e078 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Fri, 27 Mar 2015 14:59:49 -0700 Subject: [PATCH 4/6] Merge pull request #1202 from aanand/jenkins-script WIP: Jenkins script (cherry picked from commit 853ce255eac5375562e399d3e105dc5a456dbb99) Signed-off-by: Aanand Prasad --- Dockerfile | 3 +++ script/build-linux | 18 +++++++++++------- script/build-linux-inner | 10 ++++++++++ script/ci | 18 ++++++++++++++++++ script/test-versions | 5 +---- script/wrapdocker | 2 +- 6 files changed, 44 insertions(+), 12 deletions(-) create mode 100755 script/build-linux-inner create mode 100755 script/ci diff --git a/Dockerfile b/Dockerfile index d7a6019a..8ec05cc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,9 @@ RUN set -ex; \ chmod +x /usr/local/bin/docker-$v; \ done +# Set the default Docker to be run +RUN ln -s /usr/local/bin/docker-1.3.3 /usr/local/bin/docker + RUN useradd -d /home/user -m -s /bin/bash user WORKDIR /code/ diff --git a/script/build-linux b/script/build-linux index 07c9d7ec..5e4a9470 100755 --- a/script/build-linux +++ b/script/build-linux @@ -1,8 +1,12 @@ -#!/bin/sh +#!/bin/bash + set -ex -mkdir -p `pwd`/dist -chmod 777 `pwd`/dist -docker build -t docker-compose . -docker run -u user -v `pwd`/dist:/code/dist --rm --entrypoint pyinstaller docker-compose -F bin/docker-compose -mv dist/docker-compose dist/docker-compose-Linux-x86_64 -docker run -u user -v `pwd`/dist:/code/dist --rm --entrypoint dist/docker-compose-Linux-x86_64 docker-compose --version + +TAG="docker-compose" +docker build -t "$TAG" . +docker run \ + --rm \ + --user=user \ + --volume="$(pwd):/code" \ + --entrypoint="script/build-linux-inner" \ + "$TAG" diff --git a/script/build-linux-inner b/script/build-linux-inner new file mode 100755 index 00000000..34b0c06f --- /dev/null +++ b/script/build-linux-inner @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ex + +mkdir -p `pwd`/dist +chmod 777 `pwd`/dist + +pyinstaller -F bin/docker-compose +mv dist/docker-compose dist/docker-compose-Linux-x86_64 +dist/docker-compose-Linux-x86_64 --version diff --git a/script/ci b/script/ci new file mode 100755 index 00000000..a1391c62 --- /dev/null +++ b/script/ci @@ -0,0 +1,18 @@ +#!/bin/bash +# This should be run inside a container built from the Dockerfile +# at the root of the repo: +# +# $ TAG="docker-compose:$(git rev-parse --short HEAD)" +# $ docker build -t "$TAG" . +# $ docker run --rm --volume="/var/run/docker.sock:/var/run/docker.sock" --volume="$(pwd)/.git:/code/.git" -e "TAG=$TAG" --entrypoint="script/ci" "$TAG" + +set -e + +>&2 echo "Validating DCO" +script/validate-dco + +export DOCKER_VERSIONS=all +. script/test-versions + +>&2 echo "Building Linux binary" +su -c script/build-linux-inner user diff --git a/script/test-versions b/script/test-versions index a9e3bc4c..a172b9a3 100755 --- a/script/test-versions +++ b/script/test-versions @@ -4,9 +4,6 @@ set -e ->&2 echo "Validating DCO" -script/validate-dco - >&2 echo "Running lint checks" flake8 compose @@ -18,7 +15,7 @@ fi for version in $DOCKER_VERSIONS; do >&2 echo "Running tests against Docker $version" - docker-1.5.0 run \ + docker run \ --rm \ --privileged \ --volume="/var/lib/docker" \ diff --git a/script/wrapdocker b/script/wrapdocker index 20dc9e3c..7b699688 100755 --- a/script/wrapdocker +++ b/script/wrapdocker @@ -4,7 +4,7 @@ if [ "$DOCKER_VERSION" == "" ]; then DOCKER_VERSION="1.5.0" fi -ln -s "/usr/local/bin/docker-$DOCKER_VERSION" "/usr/local/bin/docker" +ln -fs "/usr/local/bin/docker-$DOCKER_VERSION" "/usr/local/bin/docker" # If a pidfile is still around (for example after a container restart), # delete it so that docker can start. From a467a8a09486e9770a4d7de4f982aeb17d8439b2 Mon Sep 17 00:00:00 2001 From: Ben Firshman Date: Thu, 9 Apr 2015 14:44:07 +0100 Subject: [PATCH 5/6] Merge pull request #1261 from aanand/fix-vars-in-volume-paths Fix vars in volume paths (cherry picked from commit 4926f8aef629631032327542a56ae35099807005) Signed-off-by: Aanand Prasad Conflicts: tests/unit/service_test.py --- compose/config.py | 2 ++ compose/service.py | 4 +--- tests/integration/service_test.py | 18 ++++++++++++++++++ tests/unit/config_test.py | 14 ++++++++++++++ tests/unit/service_test.py | 16 ---------------- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/compose/config.py b/compose/config.py index 1dc64af2..2dc59d23 100644 --- a/compose/config.py +++ b/compose/config.py @@ -328,6 +328,8 @@ def resolve_host_paths(volumes, working_dir=None): def resolve_host_path(volume, working_dir): container_path, host_path = split_volume(volume) if host_path is not None: + host_path = os.path.expanduser(host_path) + host_path = os.path.expandvars(host_path) return "%s:%s" % (expand_path(working_dir, host_path), container_path) else: return container_path diff --git a/compose/service.py b/compose/service.py index 936e3f9d..86427a1e 100644 --- a/compose/service.py +++ b/compose/service.py @@ -3,7 +3,6 @@ from __future__ import absolute_import from collections import namedtuple import logging import re -import os from operator import attrgetter import sys import six @@ -586,8 +585,7 @@ def parse_repository_tag(s): def build_volume_binding(volume_spec): internal = {'bind': volume_spec.internal, 'ro': volume_spec.mode == 'ro'} - external = os.path.expanduser(volume_spec.external) - return os.path.abspath(os.path.expandvars(external)), internal + return volume_spec.external, internal def build_port_bindings(ports): diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index f0fb771d..a89fde97 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -123,6 +123,24 @@ class ServiceTest(DockerClientTestCase): self.assertTrue(path.basename(actual_host_path) == path.basename(host_path), msg=("Last component differs: %s, %s" % (actual_host_path, host_path))) + @mock.patch.dict(os.environ) + def test_create_container_with_home_and_env_var_in_volume_path(self): + os.environ['VOLUME_NAME'] = 'my-volume' + os.environ['HOME'] = '/tmp/home-dir' + expected_host_path = os.path.join(os.environ['HOME'], os.environ['VOLUME_NAME']) + + host_path = '~/${VOLUME_NAME}' + container_path = '/container-path' + + service = self.create_service('db', volumes=['%s:%s' % (host_path, container_path)]) + container = service.create_container() + service.start_container(container) + + actual_host_path = container.get('Volumes')[container_path] + components = actual_host_path.split('/') + self.assertTrue(components[-2:] == ['home-dir', 'my-volume'], + msg="Last two components differ: %s, %s" % (actual_host_path, expected_host_path)) + def test_create_container_with_volumes_from(self): volume_service = self.create_service('data') volume_container_1 = volume_service.create_container() diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py index f25f3a9d..aa14a2a5 100644 --- a/tests/unit/config_test.py +++ b/tests/unit/config_test.py @@ -39,6 +39,20 @@ class ConfigTest(unittest.TestCase): config.make_service_dict('foo', {'ports': ['8000']}) +class VolumePathTest(unittest.TestCase): + @mock.patch.dict(os.environ) + def test_volume_binding_with_environ(self): + os.environ['VOLUME_PATH'] = '/host/path' + d = config.make_service_dict('foo', {'volumes': ['${VOLUME_PATH}:/container/path']}, working_dir='.') + self.assertEqual(d['volumes'], ['/host/path:/container/path']) + + @mock.patch.dict(os.environ) + def test_volume_binding_with_home(self): + os.environ['HOME'] = '/home/user' + d = config.make_service_dict('foo', {'volumes': ['~:/container/path']}, working_dir='.') + self.assertEqual(d['volumes'], ['/home/user:/container/path']) + + class MergeVolumesTest(unittest.TestCase): def test_empty(self): service_dict = config.merge_service_dicts({}, {}) diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index c70c30bf..24222dfe 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals from __future__ import absolute_import -import os from .. import unittest import mock @@ -301,18 +300,3 @@ class ServiceVolumesTest(unittest.TestCase): self.assertEqual( binding, ('/outside', dict(bind='/inside', ro=False))) - - @mock.patch.dict(os.environ) - def test_build_volume_binding_with_environ(self): - os.environ['VOLUME_PATH'] = '/opt' - binding = build_volume_binding(parse_volume_spec('${VOLUME_PATH}:/opt')) - self.assertEqual(binding, ('/opt', dict(bind='/opt', ro=False))) - - @mock.patch.dict(os.environ) - def test_building_volume_binding_with_home(self): - os.environ['HOME'] = '/home/user' - binding = build_volume_binding(parse_volume_spec('~:/home/user')) - self.assertEqual( - binding, - ('/home/user', dict(bind='/home/user', ro=False))) - From b3f23e2d173bdd352a8006b89e3cc70a58e84d2b Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Mon, 23 Mar 2015 10:40:23 -0700 Subject: [PATCH 6/6] Bump 1.2.0 Signed-off-by: Aanand Prasad --- CHANGES.md | 56 +++++++++++++++++++++++++++++++++++++++++++++ compose/__init__.py | 2 +- docs/completion.md | 2 +- docs/install.md | 2 +- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 75c13090..7cd60ee1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,62 @@ Change log ========== +1.2.0rc4 (2015-04-09) +--------------------- + +This is a release candidate for Compose 1.2.0. + +On top of the changes listed below for RC1-RC3, the following bugs have been fixed: + +- Environment variables and the user's home directory (~) were not expanding properly in `volumes` host paths. + +1.2.0rc3 (2015-03-23) +--------------------- + +This is a release candidate for Compose 1.2.0. + +On top of the changes listed below for RC1 and RC2, the following bugs have been fixed: + +- When copying a service's configuration with `extends`, `image` and `build` could come into conflict, resulting in an error, as it makes no sense to have both defined. Each now overwrites the other: if a service with `image` defined is extended and `build` is added, the `image` entry will be removed. + +- When copying a service's configuration with `extends`, if both services defined a multi-value option such as `ports` or `dns`, the original value would be completely discarded. They are now concatenated instead. + +- When a relative path is supplied to `build`, it is treated as relative to the *directory of the configuration file*, not the directory that `docker-compose` is being run in. In the majority of cases, those are the same, but if you use the `-f|--file` argument to specify a configuration file in another directory, **this is a breaking change**. + +1.2.0rc2 (2015-03-24) +--------------------- + +This is a release candidate for Compose 1.2.0. + +On top of the changes listed below for RC1, a bug has been fixed where containers were being created with blank entries for "Dns" and "DnsSearch", causing DNS lookups from within a container to fail. + +1.2.0rc1 (2015-03-23) +--------------------- + +This is a release candidate for Compose 1.2.0. + +- `docker-compose.yml` now supports an `extends` option, which enables a service to inherit configuration from another service in another configuration file. This is really good for sharing common configuration between apps, or for configuring the same app for different environments. Here's the [documentation](https://github.com/docker/compose/blob/master/docs/yml.md#extends). + +- When using Compose with a Swarm cluster, containers that depend on one another will be co-scheduled on the same node. This means that most Compose apps will now work out of the box, as long as they don't use `build`. + +- Repeated invocations of `docker-compose up` when using Compose with a Swarm cluster now work reliably. + +- Filenames in `env_file` and volume host paths in `volumes` are now treated as relative to the *directory of the configuration file*, not the directory that `docker-compose` is being run in. In the majority of cases, those are the same, but if you use the `-f|--file` argument to specify a configuration file in another directory, **this is a breaking change**. + +- A service can now share another service's network namespace with `net: container:`. + +- `volumes_from` and `net: container:` entries are taken into account when resolving dependencies, so `docker-compose up ` will correctly start all dependencies of ``. + +- Problems with authentication when using images from third-party registries have been fixed. + +- `docker-compose run` now accepts a `--user` argument to specify a user to run the command as, just like `docker run`. + +- The `up`, `stop` and `restart` commands now accept a `--timeout` (or `-t`) argument to specify how long to wait when attempting to gracefully stop containers, just like `docker stop`. + +- `docker-compose rm` now accepts `-f` as a shorthand for `--force`, just like `docker rm`. + +Thanks, @abesto, @albers, @alunduil, @dnephin, @funkyfuture, @gilclark, @IanVS, @KingsleyKelly, @knutwalker, @thaJeztah and @vmalloc! + 1.1.0 (2015-02-25) ------------------ diff --git a/compose/__init__.py b/compose/__init__.py index c770b395..7db5c002 100644 --- a/compose/__init__.py +++ b/compose/__init__.py @@ -1,4 +1,4 @@ from __future__ import unicode_literals from .service import Service # noqa:flake8 -__version__ = '1.1.0' +__version__ = '1.2.0rc4' diff --git a/docs/completion.md b/docs/completion.md index d9b94f6c..9fade9b2 100644 --- a/docs/completion.md +++ b/docs/completion.md @@ -17,7 +17,7 @@ On a Mac, install with `brew install bash-completion` Place the completion script in `/etc/bash_completion.d/` (`/usr/local/etc/bash_completion.d/` on a Mac), using e.g. - curl -L https://raw.githubusercontent.com/docker/compose/1.1.0/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose + curl -L https://raw.githubusercontent.com/docker/compose/1.2.0rc4/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose Completion will be available upon next login. diff --git a/docs/install.md b/docs/install.md index 0e60e1f1..5db8ea29 100644 --- a/docs/install.md +++ b/docs/install.md @@ -20,7 +20,7 @@ First, install Docker version 1.3 or greater: To install Compose, run the following commands: - curl -L https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose + curl -L https://github.com/docker/compose/releases/download/1.2.0rc4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose Optionally, you can also install [command completion](completion.md) for the