Merge pull request #7052 from ndeloof/pull_can_build

Report image we can't pull and must be built
This commit is contained in:
Ulysses Souza 2019-11-29 15:48:38 +01:00 committed by GitHub
commit e82d38f333
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 28 deletions

View File

@ -114,3 +114,13 @@ def get_digest_from_push(events):
if digest:
return digest
return None
def read_status(event):
status = event['status'].lower()
if 'progressDetail' in event:
detail = event['progressDetail']
if 'current' in detail and 'total' in detail:
percentage = float(detail['current']) / float(detail['total'])
status = '{} ({:.1%})'.format(status, percentage)
return status

View File

@ -11,6 +11,8 @@ from os import path
import enum
import six
from docker.errors import APIError
from docker.errors import ImageNotFound
from docker.errors import NotFound
from docker.utils import version_lt
from . import parallel
@ -25,6 +27,7 @@ from .container import Container
from .network import build_networks
from .network import get_networks
from .network import ProjectNetworks
from .progress_stream import read_status
from .service import BuildAction
from .service import ContainerNetworkMode
from .service import ContainerPidMode
@ -619,46 +622,68 @@ class Project(object):
def pull(self, service_names=None, ignore_pull_failures=False, parallel_pull=False, silent=False,
include_deps=False):
services = self.get_services(service_names, include_deps)
msg = not silent and 'Pulling' or None
if parallel_pull:
def pull_service(service):
strm = service.pull(ignore_pull_failures, True, stream=True)
if strm is None: # Attempting to pull service with no `image` key is a no-op
return
self.parallel_pull(services, silent=silent)
else:
must_build = []
for service in services:
try:
service.pull(ignore_pull_failures, silent=silent)
except (ImageNotFound, NotFound):
if service.can_be_built():
must_build.append(service.name)
else:
raise
if len(must_build):
log.warning('Some service image(s) must be built from source by running:\n'
' docker-compose build {}'
.format(' '.join(must_build)))
def parallel_pull(self, services, ignore_pull_failures=False, silent=False):
msg = 'Pulling' if not silent else None
must_build = []
def pull_service(service):
strm = service.pull(ignore_pull_failures, True, stream=True)
if strm is None: # Attempting to pull service with no `image` key is a no-op
return
try:
writer = parallel.get_stream_writer()
for event in strm:
if 'status' not in event:
continue
status = event['status'].lower()
if 'progressDetail' in event:
detail = event['progressDetail']
if 'current' in detail and 'total' in detail:
percentage = float(detail['current']) / float(detail['total'])
status = '{} ({:.1%})'.format(status, percentage)
status = read_status(event)
writer.write(
msg, service.name, truncate_string(status), lambda s: s
)
except (ImageNotFound, NotFound):
if service.can_be_built():
must_build.append(service.name)
else:
raise
_, errors = parallel.parallel_execute(
services,
pull_service,
operator.attrgetter('name'),
msg,
limit=5,
)
if len(errors):
combined_errors = '\n'.join([
e.decode('utf-8') if isinstance(e, six.binary_type) else e for e in errors.values()
])
raise ProjectError(combined_errors)
_, errors = parallel.parallel_execute(
services,
pull_service,
operator.attrgetter('name'),
msg,
limit=5,
)
else:
for service in services:
service.pull(ignore_pull_failures, silent=silent)
if len(must_build):
log.warning('Some service image(s) must be built from source by running:\n'
' docker-compose build {}'
.format(' '.join(must_build)))
if len(errors):
combined_errors = '\n'.join([
e.decode('utf-8') if isinstance(e, six.binary_type) else e for e in errors.values()
])
raise ProjectError(combined_errors)
def push(self, service_names=None, ignore_push_failures=False):
unique_images = set()

View File

@ -694,6 +694,14 @@ services:
result.stderr
)
def test_pull_can_build(self):
result = self.dispatch([
'-f', 'can-build-pull-failures.yml', 'pull'],
returncode=0
)
assert 'Some service image(s) must be built from source' in result.stderr
assert 'docker-compose build can_build' in result.stderr
def test_pull_with_no_deps(self):
self.base_dir = 'tests/fixtures/links-composefile'
result = self.dispatch(['pull', '--no-parallel', 'web'])

View File

@ -0,0 +1,6 @@
version: '3'
services:
can_build:
image: nonexisting-image-but-can-build:latest
build: .
command: top