From f7853a30bdb7177808a6f7366395d7bcbdcdff52 Mon Sep 17 00:00:00 2001 From: Harald Albers Date: Wed, 29 Jun 2016 08:45:36 -0700 Subject: [PATCH 01/19] bash completion for `docker-compose bundle --fetch-digests` Signed-off-by: Harald Albers --- contrib/completion/bash/docker-compose | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/bash/docker-compose b/contrib/completion/bash/docker-compose index 0adfdca847..0201bcb28a 100644 --- a/contrib/completion/bash/docker-compose +++ b/contrib/completion/bash/docker-compose @@ -117,7 +117,7 @@ _docker_compose_bundle() { ;; esac - COMPREPLY=( $( compgen -W "--help --output -o" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--fetch-digests --help --output -o" -- "$cur" ) ) } From e1b7510e4a8588df320f87ec4e24623a8e9493c4 Mon Sep 17 00:00:00 2001 From: Tomas Tomecek Date: Thu, 21 Apr 2016 14:03:02 +0200 Subject: [PATCH 02/19] service: detailed error messages for create and start Fixes: #3355 Signed-off-by: Tomas Tomecek --- compose/cli/main.py | 4 +++- compose/errors.py | 7 +++++++ compose/parallel.py | 4 ++++ compose/service.py | 12 ++++++++++-- tests/integration/service_test.py | 5 ++++- 5 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 compose/errors.py diff --git a/compose/cli/main.py b/compose/cli/main.py index f4c1716770..7ca9eac957 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -32,6 +32,7 @@ from ..service import BuildError from ..service import ConvergenceStrategy from ..service import ImageType from ..service import NeedsBuildError +from ..service import OperationFailedError from .command import get_config_from_options from .command import project_from_options from .docopt_command import DocoptDispatcher @@ -61,7 +62,8 @@ def main(): except (KeyboardInterrupt, signals.ShutdownException): log.error("Aborting.") sys.exit(1) - except (UserError, NoSuchService, ConfigurationError, ProjectError) as e: + except (UserError, NoSuchService, ConfigurationError, + ProjectError, OperationFailedError) as e: log.error(e.msg) sys.exit(1) except BuildError as e: diff --git a/compose/errors.py b/compose/errors.py new file mode 100644 index 0000000000..9f68760d32 --- /dev/null +++ b/compose/errors.py @@ -0,0 +1,7 @@ +from __future__ import absolute_import +from __future__ import unicode_literals + + +class OperationFailedError(Exception): + def __init__(self, reason): + self.msg = reason diff --git a/compose/parallel.py b/compose/parallel.py index 50b2dbeaf4..7ac66b37a0 100644 --- a/compose/parallel.py +++ b/compose/parallel.py @@ -12,6 +12,7 @@ from six.moves.queue import Empty from six.moves.queue import Queue from compose.cli.signals import ShutdownException +from compose.errors import OperationFailedError from compose.utils import get_output_stream @@ -47,6 +48,9 @@ def parallel_execute(objects, func, get_name, msg, get_deps=None): elif isinstance(exception, APIError): errors[get_name(obj)] = exception.explanation writer.write(get_name(obj), 'error') + elif isinstance(exception, OperationFailedError): + errors[get_name(obj)] = exception.msg + writer.write(get_name(obj), 'error') elif isinstance(exception, UpstreamError): writer.write(get_name(obj), 'error') else: diff --git a/compose/service.py b/compose/service.py index af572e5b5f..7bb36cd645 100644 --- a/compose/service.py +++ b/compose/service.py @@ -27,6 +27,7 @@ from .const import LABEL_PROJECT from .const import LABEL_SERVICE from .const import LABEL_VERSION from .container import Container +from .errors import OperationFailedError from .parallel import parallel_execute from .parallel import parallel_start from .progress_stream import stream_output @@ -277,7 +278,11 @@ class Service(object): if 'name' in container_options and not quiet: log.info("Creating %s" % container_options['name']) - return Container.create(self.client, **container_options) + try: + return Container.create(self.client, **container_options) + except APIError as ex: + raise OperationFailedError("Cannot create container for service %s: %s" % + (self.name, ex.explanation)) def ensure_image_exists(self, do_build=BuildAction.none): if self.can_be_built() and do_build == BuildAction.force: @@ -447,7 +452,10 @@ class Service(object): def start_container(self, container): self.connect_container_to_networks(container) - container.start() + try: + container.start() + except APIError as ex: + raise OperationFailedError("Cannot start service %s: %s" % (self.name, ex.explanation)) return container def connect_container_to_networks(self, container): diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index 1801f5bfc7..053dee1b53 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -738,7 +738,10 @@ class ServiceTest(DockerClientTestCase): self.assertEqual(len(service.containers()), 1) self.assertTrue(service.containers()[0].is_running) - self.assertIn("ERROR: for composetest_web_2 Boom", mock_stderr.getvalue()) + self.assertIn( + "ERROR: for composetest_web_2 Cannot create container for service web: Boom", + mock_stderr.getvalue() + ) def test_scale_with_unexpected_exception(self): """Test that when scaling if the API returns an error, that is not of type From 4fb7033d9cf6905a6f54d804b4ca6c48827f6fea Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Wed, 20 Jul 2016 12:23:51 +0100 Subject: [PATCH 03/19] Clarify environment and env_file docs Add note to say that environment variables will not be automatically made available at build time, and point to the `args` documentation. Signed-off-by: Aanand Prasad --- docs/compose-file.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/compose-file.md b/docs/compose-file.md index b55f250a8b..0f1fccb31d 100644 --- a/docs/compose-file.md +++ b/docs/compose-file.md @@ -274,6 +274,11 @@ beginning with `#` (i.e. comments) are ignored, as are blank lines. # Set Rails/Rack environment RACK_ENV=development +> **Note:** If your service specifies a [build](#build) option, variables +> defined in environment files will _not_ be automatically visible during the +> build. Use the [args](#args) sub-option of `build` to define build-time +> environment variables. + ### environment Add environment variables. You can use either an array or a dictionary. Any @@ -293,6 +298,11 @@ machine Compose is running on, which can be helpful for secret or host-specific - SHOW=true - SESSION_SECRET +> **Note:** If your service specifies a [build](#build) option, variables +> defined in `environment` will _not_ be automatically visible during the +> build. Use the [args](#args) sub-option of `build` to define build-time +> environment variables. + ### expose Expose ports without publishing them to the host machine - they'll only be From ad19ff6c67554168966ffaaeaa0f2c82231ca35d Mon Sep 17 00:00:00 2001 From: Jarrod Pooler Date: Fri, 8 Jul 2016 15:56:15 -0400 Subject: [PATCH 04/19] Updating arg docs in the proper place Signed-off-by: Jarrod Pooler --- docs/compose-file.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/compose-file.md b/docs/compose-file.md index 0f1fccb31d..ea1671da03 100644 --- a/docs/compose-file.md +++ b/docs/compose-file.md @@ -119,6 +119,29 @@ Add build arguments. You can use either an array or a dictionary. Any boolean values; true, false, yes, no, need to be enclosed in quotes to ensure they are not converted to True or False by the YML parser. +First, specify the arguments in your Dockerfile: + + ARG buildno + ARG password + + RUN echo "Build number: $buildno" + RUN script-requiring-password.sh "$password" + +Then specify the arguments under the `build` key. You can pass either a mapping +or a list: + + build: + context: . + args: + buildno: 1 + password: secret + + build: + context: . + args: + - buildno=1 + - password=secret + Build arguments with only a key are resolved to their environment value on the machine Compose is running on. From 8f842d55d70574ea29f635e7ab7dbaa1e64649f6 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Wed, 20 Jul 2016 12:47:47 +0100 Subject: [PATCH 05/19] Reorder/clarify args docs Signed-off-by: Aanand Prasad --- docs/compose-file.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/compose-file.md b/docs/compose-file.md index ea1671da03..d33bc20806 100644 --- a/docs/compose-file.md +++ b/docs/compose-file.md @@ -115,9 +115,8 @@ specified. > [Version 2 file format](#version-2) only. -Add build arguments. You can use either an array or a dictionary. Any -boolean values; true, false, yes, no, need to be enclosed in quotes to ensure -they are not converted to True or False by the YML parser. +Add build arguments, which are environment variables accessible only during the +build process. First, specify the arguments in your Dockerfile: @@ -142,18 +141,15 @@ or a list: - buildno=1 - password=secret -Build arguments with only a key are resolved to their environment value on the -machine Compose is running on. +You can omit the value when specifying a build argument, in which case its value +at build time is the value in the environment where Compose is running. - build: - args: - buildno: 1 - user: someuser + args: + - buildno + - password - build: - args: - - buildno=1 - - user=someuser +> **Note**: YAML boolean values (`true`, `false`, `yes`, `no`, `on`, `off`) must +> be enclosed in quotes, so that the parser interprets them as strings. ### cap_add, cap_drop From 2ecbf25445856fcf1b06a2ff56f04e4ad628042a Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Mon, 11 Jul 2016 16:13:16 -0400 Subject: [PATCH 06/19] Fix bugs with entrypoint/command in docker-compose run - When no command is passed but `--entrypoint` is, set Cmd to `[]` - When command is a single empty string, set Cmd to `[""]` Signed-off-by: Aanand Prasad --- compose/cli/main.py | 4 +- tests/acceptance/cli_test.py | 59 +++++++++++++++---- .../docker-compose.yml | 2 - .../entrypoint-composefile/docker-compose.yml | 6 ++ .../Dockerfile | 3 +- .../entrypoint-dockerfile/docker-compose.yml | 4 ++ 6 files changed, 63 insertions(+), 15 deletions(-) delete mode 100644 tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml create mode 100644 tests/fixtures/entrypoint-composefile/docker-compose.yml rename tests/fixtures/{dockerfile_with_entrypoint => entrypoint-dockerfile}/Dockerfile (57%) create mode 100644 tests/fixtures/entrypoint-dockerfile/docker-compose.yml diff --git a/compose/cli/main.py b/compose/cli/main.py index 7ca9eac957..3aa1459a38 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -670,8 +670,10 @@ class TopLevelCommand(object): 'can not be used togather' ) - if options['COMMAND']: + if options['COMMAND'] is not None: command = [options['COMMAND']] + options['ARGS'] + elif options['--entrypoint'] is not None: + command = [] else: command = service.options.get('command') diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index a8fd3249d0..ffba3002d3 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import datetime import json import os -import shlex import signal import subprocess import time @@ -965,16 +964,54 @@ class CLITestCase(DockerClientTestCase): [u'/bin/true'], ) - def test_run_service_with_entrypoint_overridden(self): - self.base_dir = 'tests/fixtures/dockerfile_with_entrypoint' - name = 'service' - self.dispatch(['run', '--entrypoint', '/bin/echo', name, 'helloworld']) - service = self.project.get_service(name) - container = service.containers(stopped=True, one_off=OneOffFilter.only)[0] - self.assertEqual( - shlex.split(container.human_readable_command), - [u'/bin/echo', u'helloworld'], - ) + def test_run_service_with_dockerfile_entrypoint(self): + self.base_dir = 'tests/fixtures/entrypoint-dockerfile' + self.dispatch(['run', 'test']) + container = self.project.containers(stopped=True, one_off=OneOffFilter.only)[0] + assert container.get('Config.Entrypoint') == ['printf'] + assert container.get('Config.Cmd') == ['default', 'args'] + + def test_run_service_with_dockerfile_entrypoint_overridden(self): + self.base_dir = 'tests/fixtures/entrypoint-dockerfile' + self.dispatch(['run', '--entrypoint', 'echo', 'test']) + container = self.project.containers(stopped=True, one_off=OneOffFilter.only)[0] + assert container.get('Config.Entrypoint') == ['echo'] + assert not container.get('Config.Cmd') + + def test_run_service_with_dockerfile_entrypoint_and_command_overridden(self): + self.base_dir = 'tests/fixtures/entrypoint-dockerfile' + self.dispatch(['run', '--entrypoint', 'echo', 'test', 'foo']) + container = self.project.containers(stopped=True, one_off=OneOffFilter.only)[0] + assert container.get('Config.Entrypoint') == ['echo'] + assert container.get('Config.Cmd') == ['foo'] + + def test_run_service_with_compose_file_entrypoint(self): + self.base_dir = 'tests/fixtures/entrypoint-composefile' + self.dispatch(['run', 'test']) + container = self.project.containers(stopped=True, one_off=OneOffFilter.only)[0] + assert container.get('Config.Entrypoint') == ['printf'] + assert container.get('Config.Cmd') == ['default', 'args'] + + def test_run_service_with_compose_file_entrypoint_overridden(self): + self.base_dir = 'tests/fixtures/entrypoint-composefile' + self.dispatch(['run', '--entrypoint', 'echo', 'test']) + container = self.project.containers(stopped=True, one_off=OneOffFilter.only)[0] + assert container.get('Config.Entrypoint') == ['echo'] + assert not container.get('Config.Cmd') + + def test_run_service_with_compose_file_entrypoint_and_command_overridden(self): + self.base_dir = 'tests/fixtures/entrypoint-composefile' + self.dispatch(['run', '--entrypoint', 'echo', 'test', 'foo']) + container = self.project.containers(stopped=True, one_off=OneOffFilter.only)[0] + assert container.get('Config.Entrypoint') == ['echo'] + assert container.get('Config.Cmd') == ['foo'] + + def test_run_service_with_compose_file_entrypoint_and_empty_string_command(self): + self.base_dir = 'tests/fixtures/entrypoint-composefile' + self.dispatch(['run', '--entrypoint', 'echo', 'test', '']) + container = self.project.containers(stopped=True, one_off=OneOffFilter.only)[0] + assert container.get('Config.Entrypoint') == ['echo'] + assert container.get('Config.Cmd') == [''] def test_run_service_with_user_overridden(self): self.base_dir = 'tests/fixtures/user-composefile' diff --git a/tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml b/tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml deleted file mode 100644 index 786315020e..0000000000 --- a/tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml +++ /dev/null @@ -1,2 +0,0 @@ -service: - build: . diff --git a/tests/fixtures/entrypoint-composefile/docker-compose.yml b/tests/fixtures/entrypoint-composefile/docker-compose.yml new file mode 100644 index 0000000000..e9880973fb --- /dev/null +++ b/tests/fixtures/entrypoint-composefile/docker-compose.yml @@ -0,0 +1,6 @@ +version: "2" +services: + test: + image: busybox + entrypoint: printf + command: default args diff --git a/tests/fixtures/dockerfile_with_entrypoint/Dockerfile b/tests/fixtures/entrypoint-dockerfile/Dockerfile similarity index 57% rename from tests/fixtures/dockerfile_with_entrypoint/Dockerfile rename to tests/fixtures/entrypoint-dockerfile/Dockerfile index e7454e59b0..49f4416c8c 100644 --- a/tests/fixtures/dockerfile_with_entrypoint/Dockerfile +++ b/tests/fixtures/entrypoint-dockerfile/Dockerfile @@ -1,3 +1,4 @@ FROM busybox:latest LABEL com.docker.compose.test_image=true -ENTRYPOINT echo "From prebuilt entrypoint" +ENTRYPOINT ["printf"] +CMD ["default", "args"] diff --git a/tests/fixtures/entrypoint-dockerfile/docker-compose.yml b/tests/fixtures/entrypoint-dockerfile/docker-compose.yml new file mode 100644 index 0000000000..8318e61f31 --- /dev/null +++ b/tests/fixtures/entrypoint-dockerfile/docker-compose.yml @@ -0,0 +1,4 @@ +version: "2" +services: + test: + build: . From f9c5816ab8dc7de4a11457966b98d5f658889c3e Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Thu, 7 Jul 2016 11:58:10 -0700 Subject: [PATCH 07/19] Stop checking the deprecated DOCKER_CLIENT_TIMEOUT variable Signed-off-by: Aanand Prasad --- compose/cli/docker_client.py | 4 ---- compose/const.py | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/compose/cli/docker_client.py b/compose/cli/docker_client.py index 3e0873c49a..bed6be7945 100644 --- a/compose/cli/docker_client.py +++ b/compose/cli/docker_client.py @@ -45,10 +45,6 @@ def docker_client(environment, version=None, tls_config=None, host=None, Returns a docker-py client configured using environment variables according to the same logic as the official Docker client. """ - if 'DOCKER_CLIENT_TIMEOUT' in environment: - log.warn("The DOCKER_CLIENT_TIMEOUT environment variable is deprecated. " - "Please use COMPOSE_HTTP_TIMEOUT instead.") - try: kwargs = kwargs_from_env(environment=environment, ssl_version=tls_version) except TLSParameterError: diff --git a/compose/const.py b/compose/const.py index 9e00d96e9f..b930e0bf0e 100644 --- a/compose/const.py +++ b/compose/const.py @@ -1,11 +1,10 @@ from __future__ import absolute_import from __future__ import unicode_literals -import os import sys DEFAULT_TIMEOUT = 10 -HTTP_TIMEOUT = int(os.environ.get('DOCKER_CLIENT_TIMEOUT', 60)) +HTTP_TIMEOUT = 60 IMAGE_EVENTS = ['delete', 'import', 'pull', 'push', 'tag', 'untag'] IS_WINDOWS_PLATFORM = (sys.platform == "win32") LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number' From b72f911ccf10bb20b9c681c2f0b02b873a29fef4 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Thu, 7 Jul 2016 12:08:47 -0700 Subject: [PATCH 08/19] Fix timeout value in error message Signed-off-by: Aanand Prasad --- compose/cli/errors.py | 7 +++---- tests/unit/cli/docker_client_test.py | 23 +++++++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/compose/cli/errors.py b/compose/cli/errors.py index 2c68d36db9..ba81867ddb 100644 --- a/compose/cli/errors.py +++ b/compose/cli/errors.py @@ -13,7 +13,6 @@ from requests.exceptions import SSLError from requests.packages.urllib3.exceptions import ReadTimeoutError from ..const import API_VERSION_TO_ENGINE_VERSION -from ..const import HTTP_TIMEOUT from .utils import call_silently from .utils import is_mac from .utils import is_ubuntu @@ -46,7 +45,7 @@ def handle_connection_errors(client): raise ConnectionError() except RequestsConnectionError as e: if e.args and isinstance(e.args[0], ReadTimeoutError): - log_timeout_error() + log_timeout_error(client.timeout) raise ConnectionError() if call_silently(['which', 'docker']) != 0: @@ -66,13 +65,13 @@ def handle_connection_errors(client): raise ConnectionError() -def log_timeout_error(): +def log_timeout_error(timeout): log.error( "An HTTP request took too long to complete. Retry with --verbose to " "obtain debug information.\n" "If you encounter this issue regularly because of slow network " "conditions, consider setting COMPOSE_HTTP_TIMEOUT to a higher " - "value (current value: %s)." % HTTP_TIMEOUT) + "value (current value: %s)." % timeout) def log_api_error(e, client_version): diff --git a/tests/unit/cli/docker_client_test.py b/tests/unit/cli/docker_client_test.py index 5334a9440a..74669d4a59 100644 --- a/tests/unit/cli/docker_client_test.py +++ b/tests/unit/cli/docker_client_test.py @@ -6,6 +6,7 @@ import os import docker import pytest +from compose.cli import errors from compose.cli.docker_client import docker_client from compose.cli.docker_client import tls_config_from_options from tests import mock @@ -19,11 +20,25 @@ class DockerClientTestCase(unittest.TestCase): del os.environ['HOME'] docker_client(os.environ) + @mock.patch.dict(os.environ) def test_docker_client_with_custom_timeout(self): - timeout = 300 - with mock.patch('compose.cli.docker_client.HTTP_TIMEOUT', 300): - client = docker_client(os.environ) - self.assertEqual(client.timeout, int(timeout)) + os.environ['COMPOSE_HTTP_TIMEOUT'] = '123' + client = docker_client(os.environ) + assert client.timeout == 123 + + @mock.patch.dict(os.environ) + def test_custom_timeout_error(self): + os.environ['COMPOSE_HTTP_TIMEOUT'] = '123' + client = docker_client(os.environ) + + with mock.patch('compose.cli.errors.log') as fake_log: + with pytest.raises(errors.ConnectionError): + with errors.handle_connection_errors(client): + raise errors.RequestsConnectionError( + errors.ReadTimeoutError(None, None, None)) + + assert fake_log.error.call_count == 1 + assert '123' in fake_log.error.call_args[0][0] class TLSConfigTestCase(unittest.TestCase): From 0488dd3709f3005ce975c3c6bbc1f0688db0cf0e Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Mon, 25 Jul 2016 16:48:28 +0100 Subject: [PATCH 09/19] Rename --fetch-digests to --push-images and remove auto-pull Signed-off-by: Aanand Prasad --- compose/bundle.py | 43 +++++++++++++++++------------------- compose/cli/main.py | 35 +++++++++++++++++++++-------- tests/unit/bundle_test.py | 46 ++++++++++++++------------------------- 3 files changed, 62 insertions(+), 62 deletions(-) diff --git a/compose/bundle.py b/compose/bundle.py index 44f6954b71..afbdabfa81 100644 --- a/compose/bundle.py +++ b/compose/bundle.py @@ -60,7 +60,7 @@ def serialize_bundle(config, image_digests): return json.dumps(to_bundle(config, image_digests), indent=2, sort_keys=True) -def get_image_digests(project, allow_fetch=False): +def get_image_digests(project, allow_push=False): digests = {} needs_push = set() needs_pull = set() @@ -69,7 +69,7 @@ def get_image_digests(project, allow_fetch=False): try: digests[service.name] = get_image_digest( service, - allow_fetch=allow_fetch, + allow_push=allow_push, ) except NeedsPush as e: needs_push.add(e.image_name) @@ -82,7 +82,7 @@ def get_image_digests(project, allow_fetch=False): return digests -def get_image_digest(service, allow_fetch=False): +def get_image_digest(service, allow_push=False): if 'image' not in service.options: raise UserError( "Service '{s.name}' doesn't define an image tag. An image name is " @@ -108,27 +108,24 @@ def get_image_digest(service, allow_fetch=False): # digests return image['RepoDigests'][0] - if not allow_fetch: - if 'build' in service.options: - raise NeedsPush(service.image_name) - else: - raise NeedsPull(service.image_name) - - return fetch_image_digest(service) - - -def fetch_image_digest(service): if 'build' not in service.options: - digest = service.pull() - else: - try: - digest = service.push() - except: - log.error( - "Failed to push image for service '{s.name}'. Please use an " - "image tag that can be pushed to a Docker " - "registry.".format(s=service)) - raise + raise NeedsPull(service.image_name) + + if not allow_push: + raise NeedsPush(service.image_name) + + return push_image(service) + + +def push_image(service): + try: + digest = service.push() + except: + log.error( + "Failed to push image for service '{s.name}'. Please use an " + "image tag that can be pushed to a Docker " + "registry.".format(s=service)) + raise if not digest: raise ValueError("Failed to get digest for %s" % service.name) diff --git a/compose/cli/main.py b/compose/cli/main.py index 3aa1459a38..b487bb7cee 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -223,15 +223,16 @@ class TopLevelCommand(object): Generate a Distributed Application Bundle (DAB) from the Compose file. Images must have digests stored, which requires interaction with a - Docker registry. If digests aren't stored for all images, you can pass - `--fetch-digests` to automatically fetch them. Images for services - with a `build` key will be pushed. Images for services without a - `build` key will be pulled. + Docker registry. If digests aren't stored for all images, you can fetch + them with `docker-compose pull` or `docker-compose push`. To push images + automatically when bundling, pass `--push-images`. Only services with + a `build` option specified will have their images pushed. Usage: bundle [options] Options: - --fetch-digests Automatically fetch image digests if missing + --push-images Automatically push images for any services + which have a `build` option specified. -o, --output PATH Path to write the bundle file to. Defaults to ".dab". @@ -247,7 +248,7 @@ class TopLevelCommand(object): try: image_digests = get_image_digests( self.project, - allow_fetch=options['--fetch-digests'], + allow_push=options['--push-images'], ) except MissingDigests as e: def list_images(images): @@ -256,12 +257,28 @@ class TopLevelCommand(object): paras = ["Some images are missing digests."] if e.needs_push: - paras += ["The following images need to be pushed:", list_images(e.needs_push)] + command_hint = ( + "Use `docker-compose push {}` to push them. " + "You can do this automatically with `docker-compose bundle --push-images`." + .format(" ".join(sorted(e.needs_push))) + ) + paras += [ + "The following images can be pushed:", + list_images(e.needs_push), + command_hint, + ] if e.needs_pull: - paras += ["The following images need to be pulled:", list_images(e.needs_pull)] + command_hint = ( + "Use `docker-compose pull {}` to pull them. " + .format(" ".join(sorted(e.needs_pull))) + ) - paras.append("If this is OK, run `docker-compose bundle --fetch-digests`.") + paras += [ + "The following images need to be pulled:", + list_images(e.needs_pull), + command_hint, + ] raise UserError("\n\n".join(paras)) diff --git a/tests/unit/bundle_test.py b/tests/unit/bundle_test.py index ff4c0dceb5..223b3b07a2 100644 --- a/tests/unit/bundle_test.py +++ b/tests/unit/bundle_test.py @@ -41,44 +41,30 @@ def test_get_image_digest_no_image(mock_service): assert "doesn't define an image tag" in exc.exconly() -def test_fetch_image_digest_for_image_with_saved_digest(mock_service): - mock_service.options['image'] = image_id = 'abcd' - mock_service.pull.return_value = expected = 'sha256:thedigest' - mock_service.image.return_value = {'RepoDigests': ['digest1']} - - digest = bundle.fetch_image_digest(mock_service) - assert digest == image_id + '@' + expected - - mock_service.pull.assert_called_once_with() - assert not mock_service.push.called - assert not mock_service.client.pull.called - - -def test_fetch_image_digest_for_image(mock_service): - mock_service.options['image'] = image_id = 'abcd' - mock_service.pull.return_value = expected = 'sha256:thedigest' - mock_service.image.return_value = {'RepoDigests': []} - - digest = bundle.fetch_image_digest(mock_service) - assert digest == image_id + '@' + expected - - mock_service.pull.assert_called_once_with() - assert not mock_service.push.called - mock_service.client.pull.assert_called_once_with(digest) - - -def test_fetch_image_digest_for_build(mock_service): +def test_push_image_with_saved_digest(mock_service): mock_service.options['build'] = '.' mock_service.options['image'] = image_id = 'abcd' mock_service.push.return_value = expected = 'sha256:thedigest' mock_service.image.return_value = {'RepoDigests': ['digest1']} - digest = bundle.fetch_image_digest(mock_service) + digest = bundle.push_image(mock_service) assert digest == image_id + '@' + expected mock_service.push.assert_called_once_with() - assert not mock_service.pull.called - assert not mock_service.client.pull.called + assert not mock_service.client.push.called + + +def test_push_image(mock_service): + mock_service.options['build'] = '.' + mock_service.options['image'] = image_id = 'abcd' + mock_service.push.return_value = expected = 'sha256:thedigest' + mock_service.image.return_value = {'RepoDigests': []} + + digest = bundle.push_image(mock_service) + assert digest == image_id + '@' + expected + + mock_service.push.assert_called_once_with() + mock_service.client.pull.assert_called_once_with(digest) def test_to_bundle(): From 606358cfb7979cd9275ea78e50e7ca22b0a7f429 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Tue, 26 Jul 2016 13:07:38 -0700 Subject: [PATCH 10/19] Update docker-py requirement to the latest release Signed-off-by: Joffrey F --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 60260e1c27..831ed65a9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ PyYAML==3.11 backports.ssl-match-hostname==3.5.0.1; python_version < '3' cached-property==1.2.0 -docker-py==1.9.0rc2 +docker-py==1.9.0 dockerpty==0.4.1 docopt==0.6.1 enum34==1.0.4; python_version < '3.4' diff --git a/setup.py b/setup.py index 3696adc624..5cb52dae4a 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ install_requires = [ 'requests >= 2.6.1, < 2.8', 'texttable >= 0.8.1, < 0.9', 'websocket-client >= 0.32.0, < 1.0', - 'docker-py == 1.9.0rc2', + 'docker-py >= 1.9.0, < 2.0', 'dockerpty >= 0.4.1, < 0.5', 'six >= 1.3.0, < 2', 'jsonschema >= 2.5.1, < 3', From 6246a2592ee95a12450c9d6487ed7fe0c877a9ea Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Fri, 24 Jun 2016 11:10:18 -0400 Subject: [PATCH 11/19] Add reference docs for push and bundle. Signed-off-by: Daniel Nephin --- docs/reference/bundle.md | 31 +++++++++++++++++++++++++++++++ docs/reference/push.md | 21 +++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 docs/reference/bundle.md create mode 100644 docs/reference/push.md diff --git a/docs/reference/bundle.md b/docs/reference/bundle.md new file mode 100644 index 0000000000..fca93a8aa6 --- /dev/null +++ b/docs/reference/bundle.md @@ -0,0 +1,31 @@ + + +# bundle + +``` +Usage: bundle [options] + +Options: + --push-images Automatically push images for any services + which have a `build` option specified. + + -o, --output PATH Path to write the bundle file to. + Defaults to ".dab". +``` + +Generate a Distributed Application Bundle (DAB) from the Compose file. + +Images must have digests stored, which requires interaction with a +Docker registry. If digests aren't stored for all images, you can fetch +them with `docker-compose pull` or `docker-compose push`. To push images +automatically when bundling, pass `--push-images`. Only services with +a `build` option specified will have their images pushed. diff --git a/docs/reference/push.md b/docs/reference/push.md new file mode 100644 index 0000000000..bdc3112e83 --- /dev/null +++ b/docs/reference/push.md @@ -0,0 +1,21 @@ + + +# push + +``` +Usage: push [options] [SERVICE...] + +Options: + --ignore-push-failures Push what it can and ignores images with push failures. +``` + +Pushes images for services. From cb076a57b9261907e61716ab073565b20cc95d0a Mon Sep 17 00:00:00 2001 From: Ben Firshman Date: Tue, 5 Jul 2016 17:29:28 +0100 Subject: [PATCH 12/19] Suggest to run Docker for Mac if it isn't running Instead of suggesting docker-machine. Signed-off-by: Ben Firshman --- compose/cli/errors.py | 30 ++++++++++++++++++++---------- compose/cli/utils.py | 4 ++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/compose/cli/errors.py b/compose/cli/errors.py index ba81867ddb..5af3ede9ff 100644 --- a/compose/cli/errors.py +++ b/compose/cli/errors.py @@ -14,6 +14,7 @@ from requests.packages.urllib3.exceptions import ReadTimeoutError from ..const import API_VERSION_TO_ENGINE_VERSION from .utils import call_silently +from .utils import is_docker_for_mac_installed from .utils import is_mac from .utils import is_ubuntu @@ -47,16 +48,7 @@ def handle_connection_errors(client): if e.args and isinstance(e.args[0], ReadTimeoutError): log_timeout_error(client.timeout) raise ConnectionError() - - if call_silently(['which', 'docker']) != 0: - if is_mac(): - exit_with_error(docker_not_found_mac) - if is_ubuntu(): - exit_with_error(docker_not_found_ubuntu) - exit_with_error(docker_not_found_generic) - if call_silently(['which', 'docker-machine']) == 0: - exit_with_error(conn_error_docker_machine) - exit_with_error(conn_error_generic.format(url=client.base_url)) + exit_with_error(get_conn_error_message(client.base_url)) except APIError as e: log_api_error(e, client.api_version) raise ConnectionError() @@ -96,6 +88,20 @@ def exit_with_error(msg): raise ConnectionError() +def get_conn_error_message(url): + if call_silently(['which', 'docker']) != 0: + if is_mac(): + return docker_not_found_mac + if is_ubuntu(): + return docker_not_found_ubuntu + return docker_not_found_generic + if is_docker_for_mac_installed(): + return conn_error_docker_for_mac + if call_silently(['which', 'docker-machine']) == 0: + return conn_error_docker_machine + return conn_error_generic.format(url=url) + + docker_not_found_mac = """ Couldn't connect to Docker daemon. You might need to install Docker: @@ -121,6 +127,10 @@ conn_error_docker_machine = """ Couldn't connect to Docker daemon - you might need to run `docker-machine start default`. """ +conn_error_docker_for_mac = """ + Couldn't connect to Docker daemon. You might need to start Docker for Mac. +""" + conn_error_generic = """ Couldn't connect to Docker daemon at {url} - is it running? diff --git a/compose/cli/utils.py b/compose/cli/utils.py index cc2b680de5..bf5df80ca7 100644 --- a/compose/cli/utils.py +++ b/compose/cli/utils.py @@ -103,3 +103,7 @@ def get_build_version(): with open(filename) as fh: return fh.read().strip() + + +def is_docker_for_mac_installed(): + return is_mac() and os.path.isdir('/Applications/Docker.app') From cd267d5121d747b282792202bac703e2b958fe97 Mon Sep 17 00:00:00 2001 From: Ben Firshman Date: Sun, 24 Jul 2016 18:57:36 +0100 Subject: [PATCH 13/19] Add user agent to API calls Signed-off-by: Ben Firshman --- compose/cli/docker_client.py | 3 +++ compose/cli/utils.py | 15 +++++++++++++++ tests/unit/cli/docker_client_test.py | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/compose/cli/docker_client.py b/compose/cli/docker_client.py index bed6be7945..ce191fbf54 100644 --- a/compose/cli/docker_client.py +++ b/compose/cli/docker_client.py @@ -10,6 +10,7 @@ from docker.utils import kwargs_from_env from ..const import HTTP_TIMEOUT from .errors import UserError +from .utils import generate_user_agent log = logging.getLogger(__name__) @@ -67,4 +68,6 @@ def docker_client(environment, version=None, tls_config=None, host=None, else: kwargs['timeout'] = HTTP_TIMEOUT + kwargs['user_agent'] = generate_user_agent() + return Client(**kwargs) diff --git a/compose/cli/utils.py b/compose/cli/utils.py index bf5df80ca7..f60f61cd04 100644 --- a/compose/cli/utils.py +++ b/compose/cli/utils.py @@ -107,3 +107,18 @@ def get_build_version(): def is_docker_for_mac_installed(): return is_mac() and os.path.isdir('/Applications/Docker.app') + + +def generate_user_agent(): + parts = [ + "docker-compose/{}".format(compose.__version__), + "docker-py/{}".format(docker.__version__), + ] + try: + p_system = platform.system() + p_release = platform.release() + except IOError: + pass + else: + parts.append("{}/{}".format(p_system, p_release)) + return " ".join(parts) diff --git a/tests/unit/cli/docker_client_test.py b/tests/unit/cli/docker_client_test.py index 74669d4a59..fc914791ca 100644 --- a/tests/unit/cli/docker_client_test.py +++ b/tests/unit/cli/docker_client_test.py @@ -2,10 +2,12 @@ from __future__ import absolute_import from __future__ import unicode_literals import os +import platform import docker import pytest +import compose from compose.cli import errors from compose.cli.docker_client import docker_client from compose.cli.docker_client import tls_config_from_options @@ -40,6 +42,16 @@ class DockerClientTestCase(unittest.TestCase): assert fake_log.error.call_count == 1 assert '123' in fake_log.error.call_args[0][0] + def test_user_agent(self): + client = docker_client(os.environ) + expected = "docker-compose/{0} docker-py/{1} {2}/{3}".format( + compose.__version__, + docker.__version__, + platform.system(), + platform.release() + ) + self.assertEqual(client.headers['User-Agent'], expected) + class TLSConfigTestCase(unittest.TestCase): ca_cert = 'tests/fixtures/tls/ca.pem' From c392acc56b1e9aee7810df6bf2671c1c040ba05e Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Fri, 1 Jul 2016 13:54:27 -0700 Subject: [PATCH 14/19] Show a warning when engine is in swarm mode Signed-off-by: Aanand Prasad --- compose/project.py | 16 ++++++++++++++++ tests/unit/project_test.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/compose/project.py b/compose/project.py index 676b6ae8c9..256fb9c08a 100644 --- a/compose/project.py +++ b/compose/project.py @@ -369,6 +369,8 @@ class Project(object): detached=False, remove_orphans=False): + warn_for_swarm_mode(self.client) + self.initialize() self.find_orphan_containers(remove_orphans) @@ -533,6 +535,20 @@ def get_volumes_from(project, service_dict): return [build_volume_from(vf) for vf in volumes_from] +def warn_for_swarm_mode(client): + info = client.info() + if info.get('Swarm', {}).get('LocalNodeState') == 'active': + log.warn( + "The Docker Engine you're using is running in swarm mode.\n\n" + "Compose does not use swarm mode to deploy services to multiple nodes in a swarm. " + "All containers will be scheduled on the current node.\n\n" + "To deploy your application across the swarm, " + "use the bundle feature of the Docker experimental build.\n\n" + "More info:\n" + "https://github.com/docker/docker/tree/master/experimental\n" + ) + + class NoSuchService(Exception): def __init__(self, name): self.name = name diff --git a/tests/unit/project_test.py b/tests/unit/project_test.py index b6a52e08d0..9569adc907 100644 --- a/tests/unit/project_test.py +++ b/tests/unit/project_test.py @@ -510,3 +510,35 @@ class ProjectTest(unittest.TestCase): project.down(ImageType.all, True) self.mock_client.remove_image.assert_called_once_with("busybox:latest") + + def test_warning_in_swarm_mode(self): + self.mock_client.info.return_value = {'Swarm': {'LocalNodeState': 'active'}} + project = Project('composetest', [], self.mock_client) + + with mock.patch('compose.project.log') as fake_log: + project.up() + assert fake_log.warn.call_count == 1 + + def test_no_warning_on_stop(self): + self.mock_client.info.return_value = {'Swarm': {'LocalNodeState': 'active'}} + project = Project('composetest', [], self.mock_client) + + with mock.patch('compose.project.log') as fake_log: + project.stop() + assert fake_log.warn.call_count == 0 + + def test_no_warning_in_normal_mode(self): + self.mock_client.info.return_value = {'Swarm': {'LocalNodeState': 'inactive'}} + project = Project('composetest', [], self.mock_client) + + with mock.patch('compose.project.log') as fake_log: + project.up() + assert fake_log.warn.call_count == 0 + + def test_no_warning_with_no_swarm_info(self): + self.mock_client.info.return_value = {} + project = Project('composetest', [], self.mock_client) + + with mock.patch('compose.project.log') as fake_log: + project.up() + assert fake_log.warn.call_count == 0 From 35ed1899819fb7f7a8acfd316aa0b76307671fed Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Mon, 25 Jul 2016 13:46:50 +0100 Subject: [PATCH 15/19] Copy experimental bundle docs into Compose docs so URL is stable Signed-off-by: Aanand Prasad --- compose/project.py | 2 +- docs/bundles.md | 199 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 docs/bundles.md diff --git a/compose/project.py b/compose/project.py index 256fb9c08a..f85e285f3d 100644 --- a/compose/project.py +++ b/compose/project.py @@ -545,7 +545,7 @@ def warn_for_swarm_mode(client): "To deploy your application across the swarm, " "use the bundle feature of the Docker experimental build.\n\n" "More info:\n" - "https://github.com/docker/docker/tree/master/experimental\n" + "https://docs.docker.com/compose/bundles\n" ) diff --git a/docs/bundles.md b/docs/bundles.md new file mode 100644 index 0000000000..0958e1ef8a --- /dev/null +++ b/docs/bundles.md @@ -0,0 +1,199 @@ + + + +# Docker Stacks and Distributed Application Bundles (experimental) + +> **Note**: This is a copy of the [Docker Stacks and Distributed Application +> Bundles](https://github.com/docker/docker/blob/v1.12.0-rc4/experimental/docker-stacks-and-bundles.md) +> document in the [docker/docker repo](https://github.com/docker/docker). + +## Overview + +Docker Stacks and Distributed Application Bundles are experimental features +introduced in Docker 1.12 and Docker Compose 1.8, alongside the concept of +swarm mode, and Nodes and Services in the Engine API. + +A Dockerfile can be built into an image, and containers can be created from +that image. Similarly, a docker-compose.yml can be built into a **distributed +application bundle**, and **stacks** can be created from that bundle. In that +sense, the bundle is a multi-services distributable image format. + +As of Docker 1.12 and Compose 1.8, the features are experimental. Neither +Docker Engine nor the Docker Registry support distribution of bundles. + +## Producing a bundle + +The easiest way to produce a bundle is to generate it using `docker-compose` +from an existing `docker-compose.yml`. Of course, that's just *one* possible way +to proceed, in the same way that `docker build` isn't the only way to produce a +Docker image. + +From `docker-compose`: + +```bash +$ docker-compose bundle +WARNING: Unsupported key 'network_mode' in services.nsqd - ignoring +WARNING: Unsupported key 'links' in services.nsqd - ignoring +WARNING: Unsupported key 'volumes' in services.nsqd - ignoring +[...] +Wrote bundle to vossibility-stack.dab +``` + +## Creating a stack from a bundle + +A stack is created using the `docker deploy` command: + +```bash +# docker deploy --help + +Usage: docker deploy [OPTIONS] STACK + +Create and update a stack + +Options: + --file string Path to a Distributed Application Bundle file (Default: STACK.dab) + --help Print usage + --with-registry-auth Send registry authentication details to Swarm agents +``` + +Let's deploy the stack created before: + +```bash +# docker deploy vossibility-stack +Loading bundle from vossibility-stack.dab +Creating service vossibility-stack_elasticsearch +Creating service vossibility-stack_kibana +Creating service vossibility-stack_logstash +Creating service vossibility-stack_lookupd +Creating service vossibility-stack_nsqd +Creating service vossibility-stack_vossibility-collector +``` + +We can verify that services were correctly created: + +```bash +# docker service ls +ID NAME REPLICAS IMAGE +COMMAND +29bv0vnlm903 vossibility-stack_lookupd 1 nsqio/nsq@sha256:eeba05599f31eba418e96e71e0984c3dc96963ceb66924dd37a47bf7ce18a662 /nsqlookupd +4awt47624qwh vossibility-stack_nsqd 1 nsqio/nsq@sha256:eeba05599f31eba418e96e71e0984c3dc96963ceb66924dd37a47bf7ce18a662 /nsqd --data-path=/data --lookupd-tcp-address=lookupd:4160 +4tjx9biia6fs vossibility-stack_elasticsearch 1 elasticsearch@sha256:12ac7c6af55d001f71800b83ba91a04f716e58d82e748fa6e5a7359eed2301aa +7563uuzr9eys vossibility-stack_kibana 1 kibana@sha256:6995a2d25709a62694a937b8a529ff36da92ebee74bafd7bf00e6caf6db2eb03 +9gc5m4met4he vossibility-stack_logstash 1 logstash@sha256:2dc8bddd1bb4a5a34e8ebaf73749f6413c101b2edef6617f2f7713926d2141fe logstash -f /etc/logstash/conf.d/logstash.conf +axqh55ipl40h vossibility-stack_vossibility-collector 1 icecrime/vossibility-collector@sha256:f03f2977203ba6253988c18d04061c5ec7aab46bca9dfd89a9a1fa4500989fba --config /config/config.toml --debug +``` + +## Managing stacks + +Stacks are managed using the `docker stack` command: + +```bash +# docker stack --help + +Usage: docker stack COMMAND + +Manage Docker stacks + +Options: + --help Print usage + +Commands: + config Print the stack configuration + deploy Create and update a stack + rm Remove the stack + services List the services in the stack + tasks List the tasks in the stack + +Run 'docker stack COMMAND --help' for more information on a command. +``` + +## Bundle file format + +Distributed application bundles are described in a JSON format. When bundles +are persisted as files, the file extension is `.dab` (Docker 1.12RC2 tools use +`.dsb` for the file extension—this will be updated in the next release client). + +A bundle has two top-level fields: `version` and `services`. The version used +by Docker 1.12 tools is `0.1`. + +`services` in the bundle are the services that comprise the app. They +correspond to the new `Service` object introduced in the 1.12 Docker Engine API. + +A service has the following fields: + +
+
+ Image (required) string +
+
+ The image that the service will run. Docker images should be referenced + with full content hash to fully specify the deployment artifact for the + service. Example: + postgres@sha256:f76245b04ddbcebab5bb6c28e76947f49222c99fec4aadb0bb + 1c24821a 9e83ef +
+
+ Command []string +
+
+ Command to run in service containers. +
+
+ Args []string +
+
+ Arguments passed to the service containers. +
+
+ Env []string +
+
+ Environment variables. +
+
+ Labels map[string]string +
+
+ Labels used for setting meta data on services. +
+
+ Ports []Port +
+
+ Service ports (composed of Port (int) and + Protocol (string). A service description can + only specify the container port to be exposed. These ports can be + mapped on runtime hosts at the operator's discretion. +
+ +
+ WorkingDir string +
+
+ Working directory inside the service containers. +
+ +
+ User string +
+
+ Username or UID (format: <name|uid>[:<group|gid>]). +
+ +
+ Networks []string +
+
+ Networks that the service containers should be connected to. An entity + deploying a bundle should create networks as needed. +
+
From 1fb5c4b15a6643f2b6a80858633343ba2f0d61c9 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Tue, 26 Jul 2016 11:51:16 +0100 Subject: [PATCH 16/19] Fix example image hash Signed-off-by: Aanand Prasad --- docs/bundles.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/bundles.md b/docs/bundles.md index 0958e1ef8a..a56adb020c 100644 --- a/docs/bundles.md +++ b/docs/bundles.md @@ -138,8 +138,7 @@ A service has the following fields: The image that the service will run. Docker images should be referenced with full content hash to fully specify the deployment artifact for the service. Example: - postgres@sha256:f76245b04ddbcebab5bb6c28e76947f49222c99fec4aadb0bb - 1c24821a 9e83ef + postgres@sha256:e0a230a9f5b4e1b8b03bb3e8cf7322b0e42b7838c5c87f4545edb48f5eb8f077
Command []string From 87b6b3c1390da4f508a6e9285a9122800581f669 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Tue, 26 Jul 2016 11:51:30 +0100 Subject: [PATCH 17/19] Add note about missing volume mount support Signed-off-by: Aanand Prasad --- docs/bundles.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/bundles.md b/docs/bundles.md index a56adb020c..1932282422 100644 --- a/docs/bundles.md +++ b/docs/bundles.md @@ -196,3 +196,6 @@ A service has the following fields: deploying a bundle should create networks as needed. + +> **Note:** Some configuration options are not yet supported in the DAB format, +> including volume mounts. From 6f3e4bbc6c6bc81c5d4d12331c7540e7dac60b20 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Tue, 26 Jul 2016 11:59:04 +0100 Subject: [PATCH 18/19] Remove note about .dsb Signed-off-by: Aanand Prasad --- docs/bundles.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/bundles.md b/docs/bundles.md index 1932282422..5ca2c1ec3c 100644 --- a/docs/bundles.md +++ b/docs/bundles.md @@ -119,8 +119,7 @@ Run 'docker stack COMMAND --help' for more information on a command. ## Bundle file format Distributed application bundles are described in a JSON format. When bundles -are persisted as files, the file extension is `.dab` (Docker 1.12RC2 tools use -`.dsb` for the file extension—this will be updated in the next release client). +are persisted as files, the file extension is `.dab`. A bundle has two top-level fields: `version` and `services`. The version used by Docker 1.12 tools is `0.1`. From cefa239c2e86ca0cbc356e4b69f809743c15c75e Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Wed, 27 Jul 2016 15:55:47 +0100 Subject: [PATCH 19/19] Bump 1.8.0-rc3 Signed-off-by: Aanand Prasad --- CHANGELOG.md | 12 +++++++++++- compose/__init__.py | 2 +- docs/install.md | 6 +++--- script/run/run.sh | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afa35820f6..8ec7d5b57b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,15 @@ Change log - As announced in 1.7.0, `docker-compose rm` now removes containers created by `docker-compose run` by default. +- Setting `entrypoint` on a service now empties out any default + command that was set on the image (i.e. any `CMD` instruction in the + Dockerfile used to build it). This makes it consistent with + the `--entrypoint` flag to `docker run`. + New Features - Added `docker-compose bundle`, a command that builds a bundle file to be consumed by the new *Docker Stack* commands in Docker 1.12. - This command automatically pushes and pulls images as needed. - Added `docker-compose push`, a command that pushes service images to a registry. @@ -27,6 +31,9 @@ Bug Fixes - Fixed a bug where Compose would erroneously try to read `.env` at the project's root when it is a directory. +- `docker-compose run -e VAR` now passes `VAR` through from the shell + to the container, as with `docker run -e VAR`. + - Improved config merging when multiple compose files are involved for several service sub-keys. @@ -52,6 +59,9 @@ Bug Fixes - Fixed a bug where errors during `docker-compose up` would show an unrelated stacktrace at the end of the process. +- `docker-compose create` and `docker-compose start` show more + descriptive error messages when something goes wrong. + 1.7.1 (2016-05-04) ----------------- diff --git a/compose/__init__.py b/compose/__init__.py index bf8a6f3068..f9f0e6a6ef 100644 --- a/compose/__init__.py +++ b/compose/__init__.py @@ -1,4 +1,4 @@ from __future__ import absolute_import from __future__ import unicode_literals -__version__ = '1.8.0-rc2' +__version__ = '1.8.0-rc3' diff --git a/docs/install.md b/docs/install.md index d1a11ab55b..2099b71c15 100644 --- a/docs/install.md +++ b/docs/install.md @@ -39,7 +39,7 @@ which the release page specifies, in your terminal. The following is an example command illustrating the format: - curl -L https://github.com/docker/compose/releases/download/1.8.0-rc2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose + curl -L https://github.com/docker/compose/releases/download/1.8.0-rc3/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose If you have problems installing with `curl`, see [Alternative Install Options](#alternative-install-options). @@ -54,7 +54,7 @@ which the release page specifies, in your terminal. 7. Test the installation. $ docker-compose --version - docker-compose version: 1.8.0-rc2 + docker-compose version: 1.8.0-rc3 ## Alternative install options @@ -77,7 +77,7 @@ to get started. Compose can also be run inside a container, from a small bash script wrapper. To install compose as a container run: - $ curl -L https://github.com/docker/compose/releases/download/1.8.0-rc2/run.sh > /usr/local/bin/docker-compose + $ curl -L https://github.com/docker/compose/releases/download/1.8.0-rc3/run.sh > /usr/local/bin/docker-compose $ chmod +x /usr/local/bin/docker-compose ## Master builds diff --git a/script/run/run.sh b/script/run/run.sh index caf6ed1194..c2c01db627 100755 --- a/script/run/run.sh +++ b/script/run/run.sh @@ -15,7 +15,7 @@ set -e -VERSION="1.8.0-rc2" +VERSION="1.8.0-rc3" IMAGE="docker/compose:$VERSION"