mirror of
https://github.com/docker/compose.git
synced 2025-07-23 05:34:36 +02:00
Report image we can't pull and must be built
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
517efbf386
commit
55c5c8e8ac
@ -114,3 +114,13 @@ def get_digest_from_push(events):
|
|||||||
if digest:
|
if digest:
|
||||||
return digest
|
return digest
|
||||||
return None
|
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
|
||||||
|
@ -11,6 +11,8 @@ from os import path
|
|||||||
import enum
|
import enum
|
||||||
import six
|
import six
|
||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
|
from docker.errors import ImageNotFound
|
||||||
|
from docker.errors import NotFound
|
||||||
from docker.utils import version_lt
|
from docker.utils import version_lt
|
||||||
|
|
||||||
from . import parallel
|
from . import parallel
|
||||||
@ -25,6 +27,7 @@ from .container import Container
|
|||||||
from .network import build_networks
|
from .network import build_networks
|
||||||
from .network import get_networks
|
from .network import get_networks
|
||||||
from .network import ProjectNetworks
|
from .network import ProjectNetworks
|
||||||
|
from .progress_stream import read_status
|
||||||
from .service import BuildAction
|
from .service import BuildAction
|
||||||
from .service import ContainerNetworkMode
|
from .service import ContainerNetworkMode
|
||||||
from .service import ContainerPidMode
|
from .service import ContainerPidMode
|
||||||
@ -619,29 +622,50 @@ class Project(object):
|
|||||||
def pull(self, service_names=None, ignore_pull_failures=False, parallel_pull=False, silent=False,
|
def pull(self, service_names=None, ignore_pull_failures=False, parallel_pull=False, silent=False,
|
||||||
include_deps=False):
|
include_deps=False):
|
||||||
services = self.get_services(service_names, include_deps)
|
services = self.get_services(service_names, include_deps)
|
||||||
msg = not silent and 'Pulling' or None
|
|
||||||
|
|
||||||
if parallel_pull:
|
if parallel_pull:
|
||||||
|
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):
|
def pull_service(service):
|
||||||
strm = service.pull(ignore_pull_failures, True, stream=True)
|
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
|
if strm is None: # Attempting to pull service with no `image` key is a no-op
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
writer = parallel.get_stream_writer()
|
writer = parallel.get_stream_writer()
|
||||||
|
|
||||||
for event in strm:
|
for event in strm:
|
||||||
if 'status' not in event:
|
if 'status' not in event:
|
||||||
continue
|
continue
|
||||||
status = event['status'].lower()
|
status = read_status(event)
|
||||||
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)
|
|
||||||
|
|
||||||
writer.write(
|
writer.write(
|
||||||
msg, service.name, truncate_string(status), lambda s: s
|
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(
|
_, errors = parallel.parallel_execute(
|
||||||
services,
|
services,
|
||||||
@ -650,16 +674,17 @@ class Project(object):
|
|||||||
msg,
|
msg,
|
||||||
limit=5,
|
limit=5,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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):
|
if len(errors):
|
||||||
combined_errors = '\n'.join([
|
combined_errors = '\n'.join([
|
||||||
e.decode('utf-8') if isinstance(e, six.binary_type) else e for e in errors.values()
|
e.decode('utf-8') if isinstance(e, six.binary_type) else e for e in errors.values()
|
||||||
])
|
])
|
||||||
raise ProjectError(combined_errors)
|
raise ProjectError(combined_errors)
|
||||||
|
|
||||||
else:
|
|
||||||
for service in services:
|
|
||||||
service.pull(ignore_pull_failures, silent=silent)
|
|
||||||
|
|
||||||
def push(self, service_names=None, ignore_push_failures=False):
|
def push(self, service_names=None, ignore_push_failures=False):
|
||||||
unique_images = set()
|
unique_images = set()
|
||||||
for service in self.get_services(service_names, include_deps=False):
|
for service in self.get_services(service_names, include_deps=False):
|
||||||
|
@ -694,6 +694,14 @@ services:
|
|||||||
result.stderr
|
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):
|
def test_pull_with_no_deps(self):
|
||||||
self.base_dir = 'tests/fixtures/links-composefile'
|
self.base_dir = 'tests/fixtures/links-composefile'
|
||||||
result = self.dispatch(['pull', '--no-parallel', 'web'])
|
result = self.dispatch(['pull', '--no-parallel', 'web'])
|
||||||
|
6
tests/fixtures/simple-composefile/can-build-pull-failures.yml
vendored
Normal file
6
tests/fixtures/simple-composefile/can-build-pull-failures.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
can_build:
|
||||||
|
image: nonexisting-image-but-can-build:latest
|
||||||
|
build: .
|
||||||
|
command: top
|
Loading…
x
Reference in New Issue
Block a user