Compare commits

...

49 Commits

Author SHA1 Message Date
Ulysses Souza
5becea4ca9 "Bump 1.29.2"
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-05-10 07:30:43 -03:00
Ulysses Souza
3ecf001f96 Merge branch 'master' into 1.29.x 2021-05-10 07:17:42 -03:00
Ulysses Souza
96a8e3b3c5 Merge pull request #8316 from ulyssessouza/bump-py
Bump py to 1.10.0
2021-05-10 07:09:21 -03:00
Ulysses Souza
aca6edd631 Merge pull request #8313 from ulyssessouza/remove-compose-v2-msg
Remove advertisement for `docker compose` from up
2021-05-10 06:55:42 -03:00
Ulysses Souza
39ab3aee50 Bump py to 1.10.0
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-05-10 06:44:49 -03:00
Ulysses Souza
39c2d42a0e Remove advertisement for docker compose from up
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-05-07 01:41:47 -03:00
Ulysses Souza
aa45dedb3d Merge pull request #8288 from aiordache/release_1.29.1
1.29.1 Post-release updates
2021-04-15 11:41:14 -03:00
Anca Iordache
afb36f236b 1.29.1 Post-release updates
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-15 15:07:50 +02:00
Ulysses Souza
f01e535cf4 Merge pull request #8290 from aiordache/flaky_tests
Update flaky tests on inter-service dependencies
2021-04-15 10:04:06 -03:00
Ulysses Souza
853efbfb54 Merge pull request #8281 from aiordache/Jenkins_update
Update node spec in Jenkinsfile and Docker config mount
2021-04-15 10:03:45 -03:00
Anca Iordache
6ea24001fa mkdir .docker directory before copying the config
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-15 14:16:05 +02:00
Anca Iordache
c3d8e558a2 update path
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-15 13:58:58 +02:00
Anca Iordache
8f9f1111f3 Copy the .docker/config to dind container for authentication
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-15 12:23:17 +02:00
Anca Iordache
1b42ecba14 Update flaky tests on inter-service dependencies
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-15 10:44:32 +02:00
Anca Iordache
c34c88b217 "Bump 1.29.1"
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-13 20:36:47 +02:00
Anca Iordache
575d67618e Merge branch 'master' into 1.29.x 2021-04-13 20:22:04 +02:00
Anca Iordache
adae403b27 Merge pull request #8261 from ulyssessouza/metrics-nosuchcommand
Ignore command names on NoSuchCommand exceptions
2021-04-13 19:38:15 +02:00
Ulysses Souza
0bea52b18d Merge pull request #8280 from aiordache/ipc_recreate
Add IPC mode to the config hash
2021-04-13 14:19:32 -03:00
Ulysses Souza
2d0c9366ff Merge pull request #8279 from aiordache/max_replicas
Add placement.max_replicas_per_node to conversion map
2021-04-13 14:18:42 -03:00
Anca Iordache
8a6dc88f9e Merge pull request #8278 from ulyssessouza/revert-scan-msg
Revert "Add Snyk scan suggestion when building"
2021-04-13 18:52:10 +02:00
Anca Iordache
d79745a2cd hardcode path to docker config
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-13 18:10:59 +02:00
Anca Iordache
1250bb7481 Update node spec in Jenkinsfile
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-13 17:11:29 +02:00
Anca Iordache
3ba41b98d3 Add Ipc mode to the config hash
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-13 16:47:42 +02:00
Anca Iordache
7f7370b811 Add placement.max_replicas_per_node to conversion map
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-13 15:55:22 +02:00
Ulysses Souza
a9e8ae190f Revert "Add Snyk scan suggestion when building"
This reverts commit e496c64127.

Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-04-13 10:34:06 -03:00
Nicolas De Loof
3eee3e093a change builder API to return imageID
this allows more flexibility running CLI builder, especially we don't
have anymore to parse build output to get build status/imageID and can
pass the console FDs to buildkit

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2021-04-09 19:29:25 +02:00
Anca Iordache
ad770b272c Merge pull request #8258 from ulyssessouza/bump-1.29.0
Bump 1.29.0
2021-04-09 14:21:03 +01:00
Ulysses Souza
e5cab3ced5 Ignore command names on NoSuchCommand exceptions
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-04-07 20:20:14 -03:00
Ulysses Souza
7984767db2 Bump 1.29.0
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-04-06 18:41:34 -03:00
Anca Iordache
0773730525 "Bump 1.29.0"
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-04-06 20:54:33 +02:00
Anca Iordache
c81046aac0 Merge pull request #8239 from timoxley/patch-1
Mention `--ansi never` in `--no-ansi` deprecation
2021-04-06 19:04:25 +01:00
Tim Kevin Oxley
84c816e887 Mention --ansi never in --no-ansi deprecation
Ideally deprecation messages should point users directly at the non-deprecated alternative so it's a no-brainer for someone to switch to it.

Signed-off-by: Tim Kevin Oxley <secoif@gmail.com>
2021-04-06 14:59:34 -03:00
Anca Iordache
1607674374 Merge pull request #8256 from ulyssessouza/bump-py-5
Bump docker-py to 5.0.0 and PyYAML to 5.4.1
2021-04-06 18:48:20 +01:00
Anca Iordache
683fac0dbf Merge pull request #8122 from ojab/add_init_containers
Add init container support
2021-04-06 18:25:24 +01:00
Ulysses Souza
ddee2958ec Bump docker-py to 5.0.0 and PyYAML to 5.4.1
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-04-06 14:24:48 -03:00
Anca Iordache
1ab1cd202b Merge pull request #8091 from docker/dependabot/pip/packaging-20.9
Bump packaging from 20.4 to 20.9
2021-04-06 18:18:25 +01:00
Anca Iordache
1752927dcd Merge pull request #8183 from docker/dependabot/pip/coverage-5.5
Bump coverage from 5.2.1 to 5.5
2021-04-06 18:13:21 +01:00
Anca Iordache
c2ddd71e5f Merge pull request #8232 from aiordache/release_1.28.6
Update changelog for 1.28.6
2021-04-06 18:11:52 +01:00
Anca Iordache
72bbd9c3a6 Merge pull request #8253 from docker/dependabot/pip/python-dotenv-0.17.0
Bump python-dotenv from 0.14.0 to 0.17.0
2021-04-06 18:10:04 +01:00
Anca Iordache
3c9ee678e7 Merge pull request #8247 from ulyssessouza/scan-suggestion
Add Snyk scan suggestion when building
2021-04-06 10:09:07 +01:00
dependabot-preview[bot]
92fefbc9cc Bump python-dotenv from 0.14.0 to 0.17.0
Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 0.14.0 to 0.17.0.
- [Release notes](https://github.com/theskumar/python-dotenv/releases)
- [Changelog](https://github.com/theskumar/python-dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/theskumar/python-dotenv/compare/v0.14.0...v0.17.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-05 21:23:08 +00:00
Ulysses Souza
e496c64127 Add Snyk scan suggestion when building
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-04-05 12:34:45 -03:00
Chris Crone
84afa518e8 Merge pull request #8061 from albers/completion-profiles
Add bash completion for profiles and a command to list profiles
2021-03-30 09:33:50 +02:00
Anca Iordache
0f6a55e036 Update changelog for 1.28.6
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
2021-03-23 15:18:15 +01:00
dependabot-preview[bot]
858ff26731 Bump coverage from 5.2.1 to 5.5
Bumps [coverage](https://github.com/nedbat/coveragepy) from 5.2.1 to 5.5.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/coverage-5.2.1...coverage-5.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-08 22:20:16 +00:00
Slava Kardakov
4daad056c4 Add init container support
Fixes #6855

See https://github.com/compose-spec/compose-spec/pull/134

Signed-off-by: Slava Kardakov <ojab@ojab.ru>
2021-02-18 17:47:59 +00:00
dependabot-preview[bot]
6209baccf3 Bump packaging from 20.4 to 20.9
Bumps [packaging](https://github.com/pypa/packaging) from 20.4 to 20.9.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/20.4...20.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-01 22:22:56 +00:00
Harald Albers
4e382b9c28 Add bash completion for --profile
Signed-off-by: Harald Albers <github@albersweb.de>
2021-01-22 19:24:15 +00:00
Harald Albers
862107a32a Add option for listing service profiles
For command completion of `docker-compose --profile`, we need a portable
way to get a list of profiles used in the current config.

This commit adds a new option `docker-compose config --profiles`.

Signed-off-by: Harald Albers <github@albersweb.de>
2021-01-22 19:24:15 +00:00
23 changed files with 343 additions and 78 deletions

View File

@@ -1,6 +1,74 @@
Change log
==========
1.29.2 (2021-05-10)
-------------------
[List of PRs / issues for this release](https://github.com/docker/compose/milestone/59?closed=1)
### Miscellaneous
- Remove advertisement for `docker compose` in the `up` command to avoid annoyance
- Bump `py` to `1.10.0` in `requirements-indirect.txt`
1.29.1 (2021-04-13)
-------------------
[List of PRs / issues for this release](https://github.com/docker/compose/milestone/58?closed=1)
### Bugs
- Fix for invalid handler warning on Windows builds
- Fix config hash to trigger container recreation on IPC mode updates
- Fix conversion map for `placement.max_replicas_per_node`
- Remove extra scan suggestion on build
1.29.0 (2021-04-06)
-------------------
[List of PRs / issues for this release](https://github.com/docker/compose/milestone/56?closed=1)
### Features
- Add profile filter to `docker-compose config`
- Add a `depends_on` condition to wait for successful service completion
### Miscellaneous
- Add image scan message on build
- Update warning message for `--no-ansi` to mention `--ansi never` as alternative
- Bump docker-py to 5.0.0
- Bump PyYAML to 5.4.1
- Bump python-dotenv to 0.17.0
1.28.6 (2021-03-23)
-------------------
[List of PRs / issues for this release](https://github.com/docker/compose/milestone/57?closed=1)
### Bugs
- Make `--env-file` relative to the current working directory and error out for invalid paths. Environment file paths set with `--env-file` are relative to the current working directory while the default `.env` file is located in the project directory which by default is the base directory of the Compose file.
- Fix missing service property `storage_opt` by updating the compose schema
- Fix build `extra_hosts` list format
- Remove extra error message on `exec`
### Miscellaneous
- Add `compose.yml` and `compose.yaml` to default filename list
1.28.5 (2021-02-25)
-------------------

2
Jenkinsfile vendored
View File

@@ -87,7 +87,7 @@ def buildImage(baseImage) {
def runTests(dockerVersion, pythonVersion, baseImage) {
return {
stage("python=${pythonVersion} docker=${dockerVersion} ${baseImage}") {
node("ubuntu && amd64 && !zfs") {
node("ubuntu-2004 && amd64 && !zfs && cgroup1") {
def scmvar = checkout(scm)
def imageName = "dockerpinata/compose:${baseImage}-${scmvar.GIT_COMMIT}"
def storageDriver = sh(script: "docker info -f \'{{.Driver}}\'", returnStdout: true).trim()

View File

@@ -1 +1 @@
__version__ = '1.29.0dev'
__version__ = '1.29.2'

View File

@@ -82,7 +82,7 @@ def main(): # noqa: C901
if not IS_LINUX_PLATFORM and command == 'help':
print("\nDocker Compose is now in the Docker CLI, try `docker compose` help")
except (KeyboardInterrupt, signals.ShutdownException):
exit_with_metrics(command, "Aborting.", status=Status.FAILURE)
exit_with_metrics(command, "Aborting.", status=Status.CANCELED)
except (UserError, NoSuchService, ConfigurationError,
ProjectError, OperationFailedError) as e:
exit_with_metrics(command, e.msg, status=Status.FAILURE)
@@ -103,7 +103,8 @@ def main(): # noqa: C901
commands = "\n".join(parse_doc_section("commands:", getdoc(e.supercommand)))
if not IS_LINUX_PLATFORM:
commands += "\n\nDocker Compose is now in the Docker CLI, try `docker compose`"
exit_with_metrics(e.command, "No such command: {}\n\n{}".format(e.command, commands))
exit_with_metrics("", log_msg="No such command: {}\n\n{}".format(
e.command, commands), status=Status.FAILURE)
except (errors.ConnectionError, StreamParseError):
exit_with_metrics(command, status=Status.FAILURE)
except SystemExit as e:
@@ -171,7 +172,8 @@ def dispatch():
if options.get("--no-ansi"):
if options.get("--ansi"):
raise UserError("--no-ansi and --ansi cannot be combined.")
log.warning('--no-ansi option is deprecated and will be removed in future versions.')
log.warning('--no-ansi option is deprecated and will be removed in future versions. '
'Use `--ansi never` instead.')
ansi_mode = AnsiMode.NEVER
setup_console_handler(console_handler,
@@ -390,6 +392,7 @@ class TopLevelCommand:
--no-interpolate Don't interpolate environment variables.
-q, --quiet Only validate the configuration, don't print
anything.
--profiles Print the profile names, one per line.
--services Print the service names, one per line.
--volumes Print the volume names, one per line.
--hash="*" Print the service config hash, one per line.
@@ -409,6 +412,15 @@ class TopLevelCommand:
if options['--quiet']:
return
if options['--profiles']:
profiles = set()
for service in compose_config.services:
if 'profiles' in service:
for profile in service['profiles']:
profiles.add(profile)
print('\n'.join(sorted(profiles)))
return
if options['--services']:
print('\n'.join(service['name'] for service in compose_config.services))
return
@@ -1132,9 +1144,6 @@ class TopLevelCommand:
attach_dependencies = options.get('--attach-dependencies')
keep_prefix = not options.get('--no-log-prefix')
if not IS_LINUX_PLATFORM:
print('Docker Compose is now in the Docker CLI, try `docker compose up`\n')
if detached and (cascade_stop or exit_value_from or attach_dependencies):
raise UserError(
"-d cannot be combined with --abort-on-container-exit or --attach-dependencies.")

View File

@@ -188,7 +188,7 @@
"properties": {
"condition": {
"type": "string",
"enum": ["service_started", "service_healthy"]
"enum": ["service_started", "service_healthy", "service_completed_successfully"]
}
},
"required": ["condition"]

View File

@@ -243,6 +243,7 @@ class ConversionMap:
service_path('healthcheck', 'disable'): to_boolean,
service_path('deploy', 'labels', PATH_JOKER): to_str,
service_path('deploy', 'replicas'): to_int,
service_path('deploy', 'placement', 'max_replicas_per_node'): to_int,
service_path('deploy', 'resources', 'limits', "cpus"): to_float,
service_path('deploy', 'update_config', 'parallelism'): to_int,
service_path('deploy', 'update_config', 'max_failure_ratio'): to_float,

View File

@@ -27,3 +27,8 @@ class NoHealthCheckConfigured(HealthCheckException):
service_name
)
)
class CompletedUnsuccessfully(Exception):
def __init__(self, container_id, exit_code):
self.msg = 'Container "{}" exited with code {}.'.format(container_id, exit_code)

View File

@@ -36,7 +36,7 @@ class MetricsCommand(requests.Session):
context_type=None, status=Status.SUCCESS,
source=MetricsSource.CLI, uri=None):
super().__init__()
self.command = "compose " + command if command else "compose --help"
self.command = ("compose " + command).strip() if command else "compose --help"
self.context = context_type or ContextAPI.get_current_context().context_type or 'moby'
self.source = source
self.status = status.value

View File

@@ -16,6 +16,7 @@ from compose.cli.colors import green
from compose.cli.colors import red
from compose.cli.signals import ShutdownException
from compose.const import PARALLEL_LIMIT
from compose.errors import CompletedUnsuccessfully
from compose.errors import HealthCheckFailed
from compose.errors import NoHealthCheckConfigured
from compose.errors import OperationFailedError
@@ -61,7 +62,8 @@ def parallel_execute_watch(events, writer, errors, results, msg, get_name, fail_
elif isinstance(exception, APIError):
errors[get_name(obj)] = exception.explanation
writer.write(msg, get_name(obj), 'error', red)
elif isinstance(exception, (OperationFailedError, HealthCheckFailed, NoHealthCheckConfigured)):
elif isinstance(exception, (OperationFailedError, HealthCheckFailed, NoHealthCheckConfigured,
CompletedUnsuccessfully)):
errors[get_name(obj)] = exception.msg
writer.write(msg, get_name(obj), 'error', red)
elif isinstance(exception, UpstreamError):
@@ -241,6 +243,12 @@ def feed_queue(objects, func, get_deps, results, state, limiter):
'not processing'.format(obj)
)
results.put((obj, None, e))
except CompletedUnsuccessfully as e:
log.debug(
'Service(s) upstream of {} did not completed successfully - '
'not processing'.format(obj)
)
results.put((obj, None, e))
if state.is_done():
results.put(STOP)

View File

@@ -1,6 +1,5 @@
import enum
import itertools
import json
import logging
import os
import re
@@ -45,6 +44,7 @@ from .const import LABEL_VERSION
from .const import NANOCPUS_SCALE
from .const import WINDOWS_LONGPATH_PREFIX
from .container import Container
from .errors import CompletedUnsuccessfully
from .errors import HealthCheckFailed
from .errors import NoHealthCheckConfigured
from .errors import OperationFailedError
@@ -112,6 +112,7 @@ HOST_CONFIG_KEYS = [
CONDITION_STARTED = 'service_started'
CONDITION_HEALTHY = 'service_healthy'
CONDITION_COMPLETED_SUCCESSFULLY = 'service_completed_successfully'
class BuildError(Exception):
@@ -712,6 +713,7 @@ class Service:
'image_id': image_id(),
'links': self.get_link_names(),
'net': self.network_mode.id,
'ipc_mode': self.ipc_mode.mode,
'networks': self.networks,
'secrets': self.secrets,
'volumes_from': [
@@ -753,6 +755,8 @@ class Service:
configs[svc] = lambda s: True
elif config['condition'] == CONDITION_HEALTHY:
configs[svc] = lambda s: s.is_healthy()
elif config['condition'] == CONDITION_COMPLETED_SUCCESSFULLY:
configs[svc] = lambda s: s.is_completed_successfully()
else:
# The config schema already prevents this, but it might be
# bypassed if Compose is called programmatically.
@@ -1103,8 +1107,9 @@ class Service:
'Impossible to perform platform-targeted builds for API version < 1.35'
)
builder = self.client if not cli else _CLIBuilder(progress)
build_output = builder.build(
builder = _ClientBuilder(self.client) if not cli else _CLIBuilder(progress)
return builder.build(
service=self,
path=path,
tag=self.image_name,
rm=rm,
@@ -1125,30 +1130,7 @@ class Service:
gzip=gzip,
isolation=build_opts.get('isolation', self.options.get('isolation', None)),
platform=self.platform,
)
try:
all_events = list(stream_output(build_output, output_stream))
except StreamOutputError as e:
raise BuildError(self, str(e))
# Ensure the HTTP connection is not reused for another
# streaming command, as the Docker daemon can sometimes
# complain about it
self.client.close()
image_id = None
for event in all_events:
if 'stream' in event:
match = re.search(r'Successfully built ([0-9a-f]+)', event.get('stream', ''))
if match:
image_id = match.group(1)
if image_id is None:
raise BuildError(self, event if all_events else 'Unknown')
return image_id
output_stream=output_stream)
def get_cache_from(self, build_opts):
cache_from = build_opts.get('cache_from', None)
@@ -1304,6 +1286,21 @@ class Service:
raise HealthCheckFailed(ctnr.short_id)
return result
def is_completed_successfully(self):
""" Check that all containers for this service has completed successfully
Returns false if at least one container does not exited and
raises CompletedUnsuccessfully exception if at least one container
exited with non-zero exit code.
"""
result = True
for ctnr in self.containers(stopped=True):
ctnr.inspect()
if ctnr.get('State.Status') != 'exited':
result = False
elif ctnr.exit_code != 0:
raise CompletedUnsuccessfully(ctnr.short_id, ctnr.exit_code)
return result
def _parse_proxy_config(self):
client = self.client
if 'proxies' not in client._general_configs:
@@ -1790,20 +1787,77 @@ def rewrite_build_path(path):
return path
class _CLIBuilder:
def __init__(self, progress):
self._progress = progress
class _ClientBuilder:
def __init__(self, client):
self.client = client
def build(self, path, tag=None, quiet=False, fileobj=None,
def build(self, service, path, tag=None, quiet=False, fileobj=None,
nocache=False, rm=False, timeout=None,
custom_context=False, encoding=None, pull=False,
forcerm=False, dockerfile=None, container_limits=None,
decode=False, buildargs=None, gzip=False, shmsize=None,
labels=None, cache_from=None, target=None, network_mode=None,
squash=None, extra_hosts=None, platform=None, isolation=None,
use_config_proxy=True):
use_config_proxy=True, output_stream=sys.stdout):
build_output = self.client.build(
path=path,
tag=tag,
nocache=nocache,
rm=rm,
pull=pull,
forcerm=forcerm,
dockerfile=dockerfile,
labels=labels,
cache_from=cache_from,
buildargs=buildargs,
network_mode=network_mode,
target=target,
shmsize=shmsize,
extra_hosts=extra_hosts,
container_limits=container_limits,
gzip=gzip,
isolation=isolation,
platform=platform)
try:
all_events = list(stream_output(build_output, output_stream))
except StreamOutputError as e:
raise BuildError(service, str(e))
# Ensure the HTTP connection is not reused for another
# streaming command, as the Docker daemon can sometimes
# complain about it
self.client.close()
image_id = None
for event in all_events:
if 'stream' in event:
match = re.search(r'Successfully built ([0-9a-f]+)', event.get('stream', ''))
if match:
image_id = match.group(1)
if image_id is None:
raise BuildError(service, event if all_events else 'Unknown')
return image_id
class _CLIBuilder:
def __init__(self, progress):
self._progress = progress
def build(self, service, path, tag=None, quiet=False, fileobj=None,
nocache=False, rm=False, timeout=None,
custom_context=False, encoding=None, pull=False,
forcerm=False, dockerfile=None, container_limits=None,
decode=False, buildargs=None, gzip=False, shmsize=None,
labels=None, cache_from=None, target=None, network_mode=None,
squash=None, extra_hosts=None, platform=None, isolation=None,
use_config_proxy=True, output_stream=sys.stdout):
"""
Args:
service (str): Service to be built
path (str): Path to the directory containing the Dockerfile
buildargs (dict): A dictionary of build arguments
cache_from (:py:class:`list`): A list of images used for build
@@ -1852,6 +1906,7 @@ class _CLIBuilder:
configuration file (``~/.docker/config.json`` by default)
contains a proxy configuration, the corresponding environment
variables will be set in the container being built.
output_stream (writer): stream to use for build logs
Returns:
A generator for the build output.
"""
@@ -1884,33 +1939,18 @@ class _CLIBuilder:
args = command_builder.build([path])
magic_word = "Successfully built "
appear = False
with subprocess.Popen(args, stdout=subprocess.PIPE,
with subprocess.Popen(args, stdout=output_stream, stderr=sys.stderr,
universal_newlines=True) as p:
while True:
line = p.stdout.readline()
if not line:
break
if line.startswith(magic_word):
appear = True
yield json.dumps({"stream": line})
p.communicate()
if p.returncode != 0:
raise StreamOutputError()
raise BuildError(service, "Build failed")
with open(iidfile) as f:
line = f.readline()
image_id = line.split(":")[1].strip()
os.remove(iidfile)
# In case of `DOCKER_BUILDKIT=1`
# there is no success message already present in the output.
# Since that's the way `Service::build` gets the `image_id`
# it has to be added `manually`
if not appear:
yield json.dumps({"stream": "{}{}\n".format(magic_word, image_id)})
return image_id
class _CommandBuilder:

View File

@@ -138,7 +138,7 @@ _docker_compose_config() {
;;
esac
COMPREPLY=( $( compgen -W "--hash --help --no-interpolate --quiet -q --resolve-image-digests --services --volumes" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--hash --help --no-interpolate --profiles --quiet -q --resolve-image-digests --services --volumes" -- "$cur" ) )
}
@@ -172,6 +172,10 @@ _docker_compose_docker_compose() {
COMPREPLY=( $( compgen -W "debug info warning error critical" -- "$cur" ) )
return
;;
--profile)
COMPREPLY=( $( compgen -W "$(__docker_compose_q config --profiles)" -- "$cur" ) )
return
;;
--project-directory)
_filedir -d
return
@@ -618,10 +622,11 @@ _docker_compose() {
--tlskey
"
# These options are require special treatment when searching the command.
# These options require special treatment when searching the command.
local top_level_options_with_args="
--ansi
--log-level
--profile
"
COMPREPLY=()

View File

@@ -1,5 +1,5 @@
Click==7.1.2
coverage==5.2.1
coverage==5.5
ddt==1.4.1
flake8==3.8.3
gitpython==3.1.11
@@ -7,4 +7,3 @@ mock==3.0.5
pytest==6.0.1; python_version >= '3.5'
pytest==4.6.5; python_version < '3.5'
pytest-cov==2.10.1
PyYAML==5.3.1

View File

@@ -11,9 +11,9 @@ gitdb2==4.0.2
mccabe==0.6.1
more-itertools==8.6.0; python_version >= '3.5'
more-itertools==5.0.0; python_version < '3.5'
packaging==20.4
packaging==20.9
pluggy==0.13.1
py==1.9.0
py==1.10.0
pycodestyle==2.6.0
pycparser==2.20
pyflakes==2.2.0

View File

@@ -4,7 +4,7 @@ certifi==2020.6.20
chardet==3.0.4
colorama==0.4.3; sys_platform == 'win32'
distro==1.5.0
docker==4.4.4
docker==5.0.0
docker-pycreds==0.4.0
dockerpty==0.4.1
docopt==0.6.2
@@ -13,9 +13,9 @@ ipaddress==1.0.23
jsonschema==3.2.0
paramiko==2.7.1
PySocks==1.7.1
python-dotenv==0.14.0
python-dotenv==0.17.0
pywin32==227; sys_platform == 'win32'
PyYAML==5.3.1
PyYAML==5.4.1
requests==2.24.0
texttable==1.6.2
urllib3==1.25.10; python_version == '3.3'

0
script/release/release.py Normal file → Executable file
View File

View File

@@ -15,7 +15,7 @@
set -e
VERSION="1.26.1"
VERSION="1.29.2"
IMAGE="docker/compose:$VERSION"

View File

@@ -43,7 +43,6 @@ for version in $DOCKER_VERSIONS; do
--name "$daemon_container" \
--privileged \
--volume="/var/lib/docker" \
-v $DOCKER_CONFIG/config.json:/root/.docker/config.json \
-e "DOCKER_TLS_CERTDIR=" \
"docker:$version-dind" \
dockerd -H tcp://0.0.0.0:2375 $DOCKER_DAEMON_ARGS \
@@ -51,6 +50,11 @@ for version in $DOCKER_VERSIONS; do
docker exec "$daemon_container" sh -c "apk add --no-cache git"
# copy docker config from host for authentication with Docker Hub
docker exec "$daemon_container" sh -c "mkdir /root/.docker"
docker cp /root/.docker/config.json $daemon_container:/root/.docker/config.json
docker exec "$daemon_container" sh -c "chmod 644 /root/.docker/config.json"
docker run \
--rm \
--tty \

View File

@@ -31,7 +31,7 @@ install_requires = [
'texttable >= 0.9.0, < 2',
'websocket-client >= 0.32.0, < 1',
'distro >= 1.5.0, < 2',
'docker[ssh] >= 4.4.4, < 5',
'docker[ssh] >= 5',
'dockerpty >= 0.4.1, < 1',
'jsonschema >= 2.5.1, < 4',
'python-dotenv >= 0.13.0, < 1',

View File

@@ -237,6 +237,11 @@ class CLITestCase(DockerClientTestCase):
result = self.dispatch(['-H=tcp://doesnotexist:8000', 'ps'], returncode=1)
assert "Couldn't connect to Docker daemon" in result.stderr
def test_config_list_profiles(self):
self.base_dir = 'tests/fixtures/config-profiles'
result = self.dispatch(['config', '--profiles'])
assert set(result.stdout.rstrip().split('\n')) == {'debug', 'frontend', 'gui'}
def test_config_list_services(self):
self.base_dir = 'tests/fixtures/v2-full'
result = self.dispatch(['config', '--services'])

View File

@@ -0,0 +1,15 @@
version: '3.8'
services:
frontend:
image: frontend
profiles: ["frontend", "gui"]
phpmyadmin:
image: phpmyadmin
depends_on:
- db
profiles:
- debug
backend:
image: backend
db:
image: mysql

View File

@@ -25,6 +25,7 @@ from compose.const import COMPOSE_SPEC as VERSION
from compose.const import LABEL_PROJECT
from compose.const import LABEL_SERVICE
from compose.container import Container
from compose.errors import CompletedUnsuccessfully
from compose.errors import HealthCheckFailed
from compose.errors import NoHealthCheckConfigured
from compose.project import Project
@@ -1899,6 +1900,106 @@ class ProjectTest(DockerClientTestCase):
with pytest.raises(NoHealthCheckConfigured):
svc1.is_healthy()
def test_project_up_completed_successfully_dependency(self):
config_dict = {
'version': '2.1',
'services': {
'svc1': {
'image': BUSYBOX_IMAGE_WITH_TAG,
'command': 'true'
},
'svc2': {
'image': BUSYBOX_IMAGE_WITH_TAG,
'command': 'top',
'depends_on': {
'svc1': {'condition': 'service_completed_successfully'},
}
}
}
}
config_data = load_config(config_dict)
project = Project.from_config(
name='composetest', config_data=config_data, client=self.client
)
project.up()
svc1 = project.get_service('svc1')
svc2 = project.get_service('svc2')
assert 'svc1' in svc2.get_dependency_names()
assert svc2.containers()[0].is_running
assert len(svc1.containers()) == 0
assert svc1.is_completed_successfully()
def test_project_up_completed_unsuccessfully_dependency(self):
config_dict = {
'version': '2.1',
'services': {
'svc1': {
'image': BUSYBOX_IMAGE_WITH_TAG,
'command': 'false'
},
'svc2': {
'image': BUSYBOX_IMAGE_WITH_TAG,
'command': 'top',
'depends_on': {
'svc1': {'condition': 'service_completed_successfully'},
}
}
}
}
config_data = load_config(config_dict)
project = Project.from_config(
name='composetest', config_data=config_data, client=self.client
)
with pytest.raises(ProjectError):
project.up()
svc1 = project.get_service('svc1')
svc2 = project.get_service('svc2')
assert 'svc1' in svc2.get_dependency_names()
assert len(svc2.containers()) == 0
with pytest.raises(CompletedUnsuccessfully):
svc1.is_completed_successfully()
def test_project_up_completed_differently_dependencies(self):
config_dict = {
'version': '2.1',
'services': {
'svc1': {
'image': BUSYBOX_IMAGE_WITH_TAG,
'command': 'true'
},
'svc2': {
'image': BUSYBOX_IMAGE_WITH_TAG,
'command': 'false'
},
'svc3': {
'image': BUSYBOX_IMAGE_WITH_TAG,
'command': 'top',
'depends_on': {
'svc1': {'condition': 'service_completed_successfully'},
'svc2': {'condition': 'service_completed_successfully'},
}
}
}
}
config_data = load_config(config_dict)
project = Project.from_config(
name='composetest', config_data=config_data, client=self.client
)
with pytest.raises(ProjectError):
project.up()
svc1 = project.get_service('svc1')
svc2 = project.get_service('svc2')
svc3 = project.get_service('svc3')
assert ['svc1', 'svc2'] == svc3.get_dependency_names()
assert svc1.is_completed_successfully()
assert len(svc3.containers()) == 0
with pytest.raises(CompletedUnsuccessfully):
svc2.is_completed_successfully()
def test_project_up_seccomp_profile(self):
seccomp_data = {
'defaultAction': 'SCMP_ACT_ALLOW',

View File

@@ -2397,7 +2397,8 @@ web:
'image': 'busybox',
'depends_on': {
'app1': {'condition': 'service_started'},
'app2': {'condition': 'service_healthy'}
'app2': {'condition': 'service_healthy'},
'app3': {'condition': 'service_completed_successfully'}
}
}
override = {}
@@ -2409,11 +2410,12 @@ web:
'image': 'busybox',
'depends_on': {
'app1': {'condition': 'service_started'},
'app2': {'condition': 'service_healthy'}
'app2': {'condition': 'service_healthy'},
'app3': {'condition': 'service_completed_successfully'}
}
}
override = {
'depends_on': ['app3']
'depends_on': ['app4']
}
actual = config.merge_service_dicts(base, override, VERSION)
@@ -2422,7 +2424,8 @@ web:
'depends_on': {
'app1': {'condition': 'service_started'},
'app2': {'condition': 'service_healthy'},
'app3': {'condition': 'service_started'}
'app3': {'condition': 'service_completed_successfully'},
'app4': {'condition': 'service_started'},
}
}

View File

@@ -330,7 +330,7 @@ class ServiceTest(unittest.TestCase):
assert service.options['environment'] == environment
assert opts['labels'][LABEL_CONFIG_HASH] == \
'689149e6041a85f6fb4945a2146a497ed43c8a5cbd8991753d875b165f1b4de4'
'6da0f3ec0d5adf901de304bdc7e0ee44ec5dd7adb08aebc20fe0dd791d4ee5a8'
assert opts['environment'] == ['also=real']
def test_get_container_create_options_sets_affinity_with_binds(self):
@@ -700,6 +700,7 @@ class ServiceTest(unittest.TestCase):
config_dict = service.config_dict()
expected = {
'image_id': 'abcd',
'ipc_mode': None,
'options': {'image': 'example.com/foo'},
'links': [('one', 'one')],
'net': 'other',
@@ -723,6 +724,7 @@ class ServiceTest(unittest.TestCase):
config_dict = service.config_dict()
expected = {
'image_id': 'abcd',
'ipc_mode': None,
'options': {'image': 'example.com/foo'},
'links': [],
'networks': {},