feat: add --progress flag

Signed-off-by: Nao YONASHIRO <yonashiro@r.recruit.co.jp>
This commit is contained in:
Nao YONASHIRO 2019-08-28 05:34:47 +09:00 committed by Ulysses Souza
parent 862a13b8f3
commit 81e223d499
3 changed files with 100 additions and 90 deletions

View File

@ -270,6 +270,9 @@ class TopLevelCommand(object):
--no-cache Do not use cache when building the image. --no-cache Do not use cache when building the image.
--no-rm Do not remove intermediate containers after a successful build. --no-rm Do not remove intermediate containers after a successful build.
--parallel Build images in parallel. --parallel Build images in parallel.
--progress string Set type of progress output (auto, plain, tty).
EXPERIMENTAL flag for native builder.
To enable, run with COMPOSE_NATIVE_BUILDER=1)
--pull Always attempt to pull a newer version of the image. --pull Always attempt to pull a newer version of the image.
-q, --quiet Don't print anything to STDOUT -q, --quiet Don't print anything to STDOUT
""" """
@ -297,6 +300,7 @@ class TopLevelCommand(object):
parallel_build=options.get('--parallel', False), parallel_build=options.get('--parallel', False),
silent=options.get('--quiet', False), silent=options.get('--quiet', False),
cli=native_builder, cli=native_builder,
progress=options.get('--progress'),
) )
def bundle(self, options): def bundle(self, options):

View File

@ -355,7 +355,8 @@ class Project(object):
return containers return containers
def build(self, service_names=None, no_cache=False, pull=False, force_rm=False, memory=None, def build(self, service_names=None, no_cache=False, pull=False, force_rm=False, memory=None,
build_args=None, gzip=False, parallel_build=False, rm=True, silent=False, cli=False): build_args=None, gzip=False, parallel_build=False, rm=True, silent=False, cli=False,
progress=None):
services = [] services = []
for service in self.get_services(service_names): for service in self.get_services(service_names):
@ -368,7 +369,7 @@ class Project(object):
log.warning("Native build is an experimental feature and could change at any time") log.warning("Native build is an experimental feature and could change at any time")
def build_service(service): def build_service(service):
service.build(no_cache, pull, force_rm, memory, build_args, gzip, rm, silent, cli) service.build(no_cache, pull, force_rm, memory, build_args, gzip, rm, silent, cli, progress)
if parallel_build: if parallel_build:
_, errors = parallel.parallel_execute( _, errors = parallel.parallel_execute(
services, services,

View File

@ -1052,7 +1052,7 @@ class Service(object):
return [build_spec(secret) for secret in self.secrets] return [build_spec(secret) for secret in self.secrets]
def build(self, no_cache=False, pull=False, force_rm=False, memory=None, build_args_override=None, def build(self, no_cache=False, pull=False, force_rm=False, memory=None, build_args_override=None,
gzip=False, rm=True, silent=False, cli=False): gzip=False, rm=True, silent=False, cli=False, progress=None):
output_stream = open(os.devnull, 'w') output_stream = open(os.devnull, 'w')
if not silent: if not silent:
output_stream = sys.stdout output_stream = sys.stdout
@ -1073,8 +1073,8 @@ class Service(object):
'Impossible to perform platform-targeted builds for API version < 1.35' 'Impossible to perform platform-targeted builds for API version < 1.35'
) )
build_image = self.client.build if not cli else cli_build builder = self.client if not cli else _CLIBuilder(progress)
build_output = build_image( build_output = builder.build(
path=path, path=path,
tag=self.image_name, tag=self.image_name,
rm=rm, rm=rm,
@ -1707,7 +1707,11 @@ def rewrite_build_path(path):
return path return path
def cli_build(path, tag=None, quiet=False, fileobj=None, class _CLIBuilder(object):
def __init__(self, progress):
self._progress = progress
def build(self, path, tag=None, quiet=False, fileobj=None,
nocache=False, rm=False, timeout=None, nocache=False, rm=False, timeout=None,
custom_context=False, encoding=None, pull=False, custom_context=False, encoding=None, pull=False,
forcerm=False, dockerfile=None, container_limits=None, forcerm=False, dockerfile=None, container_limits=None,
@ -1715,94 +1719,95 @@ def cli_build(path, tag=None, quiet=False, fileobj=None,
labels=None, cache_from=None, target=None, network_mode=None, labels=None, cache_from=None, target=None, network_mode=None,
squash=None, extra_hosts=None, platform=None, isolation=None, squash=None, extra_hosts=None, platform=None, isolation=None,
use_config_proxy=True): use_config_proxy=True):
""" """
Args: Args:
path (str): Path to the directory containing the Dockerfile path (str): Path to the directory containing the Dockerfile
buildargs (dict): A dictionary of build arguments buildargs (dict): A dictionary of build arguments
cache_from (:py:class:`list`): A list of images used for build cache_from (:py:class:`list`): A list of images used for build
cache resolution cache resolution
container_limits (dict): A dictionary of limits applied to each container_limits (dict): A dictionary of limits applied to each
container created by the build process. Valid keys: container created by the build process. Valid keys:
- memory (int): set memory limit for build - memory (int): set memory limit for build
- memswap (int): Total memory (memory + swap), -1 to disable - memswap (int): Total memory (memory + swap), -1 to disable
swap swap
- cpushares (int): CPU shares (relative weight) - cpushares (int): CPU shares (relative weight)
- cpusetcpus (str): CPUs in which to allow execution, e.g., - cpusetcpus (str): CPUs in which to allow execution, e.g.,
``"0-3"``, ``"0,1"`` ``"0-3"``, ``"0,1"``
custom_context (bool): Optional if using ``fileobj`` custom_context (bool): Optional if using ``fileobj``
decode (bool): If set to ``True``, the returned stream will be decode (bool): If set to ``True``, the returned stream will be
decoded into dicts on the fly. Default ``False`` decoded into dicts on the fly. Default ``False``
dockerfile (str): path within the build context to the Dockerfile dockerfile (str): path within the build context to the Dockerfile
encoding (str): The encoding for a stream. Set to ``gzip`` for encoding (str): The encoding for a stream. Set to ``gzip`` for
compressing compressing
extra_hosts (dict): Extra hosts to add to /etc/hosts in building extra_hosts (dict): Extra hosts to add to /etc/hosts in building
containers, as a mapping of hostname to IP address. containers, as a mapping of hostname to IP address.
fileobj: A file object to use as the Dockerfile. (Or a file-like fileobj: A file object to use as the Dockerfile. (Or a file-like
object) object)
forcerm (bool): Always remove intermediate containers, even after forcerm (bool): Always remove intermediate containers, even after
unsuccessful builds unsuccessful builds
isolation (str): Isolation technology used during build. isolation (str): Isolation technology used during build.
Default: `None`. Default: `None`.
labels (dict): A dictionary of labels to set on the image labels (dict): A dictionary of labels to set on the image
network_mode (str): networking mode for the run commands during network_mode (str): networking mode for the run commands during
build build
nocache (bool): Don't use the cache when set to ``True`` nocache (bool): Don't use the cache when set to ``True``
platform (str): Platform in the format ``os[/arch[/variant]]`` platform (str): Platform in the format ``os[/arch[/variant]]``
pull (bool): Downloads any updates to the FROM image in Dockerfiles pull (bool): Downloads any updates to the FROM image in Dockerfiles
quiet (bool): Whether to return the status quiet (bool): Whether to return the status
rm (bool): Remove intermediate containers. The ``docker build`` rm (bool): Remove intermediate containers. The ``docker build``
command now defaults to ``--rm=true``, but we have kept the old command now defaults to ``--rm=true``, but we have kept the old
default of `False` to preserve backward compatibility default of `False` to preserve backward compatibility
shmsize (int): Size of `/dev/shm` in bytes. The size must be shmsize (int): Size of `/dev/shm` in bytes. The size must be
greater than 0. If omitted the system uses 64MB greater than 0. If omitted the system uses 64MB
squash (bool): Squash the resulting images layers into a squash (bool): Squash the resulting images layers into a
single layer. single layer.
tag (str): A tag to add to the final image tag (str): A tag to add to the final image
target (str): Name of the build-stage to build in a multi-stage target (str): Name of the build-stage to build in a multi-stage
Dockerfile Dockerfile
timeout (int): HTTP timeout timeout (int): HTTP timeout
use_config_proxy (bool): If ``True``, and if the docker client use_config_proxy (bool): If ``True``, and if the docker client
configuration file (``~/.docker/config.json`` by default) configuration file (``~/.docker/config.json`` by default)
contains a proxy configuration, the corresponding environment contains a proxy configuration, the corresponding environment
variables will be set in the container being built. variables will be set in the container being built.
Returns: Returns:
A generator for the build output. A generator for the build output.
""" """
if dockerfile: if dockerfile:
dockerfile = os.path.join(path, dockerfile) dockerfile = os.path.join(path, dockerfile)
iidfile = tempfile.mktemp() iidfile = tempfile.mktemp()
command_builder = _CommandBuilder() command_builder = _CommandBuilder()
command_builder.add_params("--build-arg", buildargs) command_builder.add_params("--build-arg", buildargs)
command_builder.add_list("--cache-from", cache_from) command_builder.add_list("--cache-from", cache_from)
command_builder.add_arg("--file", dockerfile) command_builder.add_arg("--file", dockerfile)
command_builder.add_flag("--force-rm", forcerm) command_builder.add_flag("--force-rm", forcerm)
command_builder.add_arg("--memory", container_limits.get("memory")) command_builder.add_arg("--memory", container_limits.get("memory"))
command_builder.add_flag("--no-cache", nocache) command_builder.add_flag("--no-cache", nocache)
command_builder.add_flag("--pull", pull) command_builder.add_flag("--progress", self._progress)
command_builder.add_arg("--tag", tag) command_builder.add_flag("--pull", pull)
command_builder.add_arg("--target", target) command_builder.add_arg("--tag", tag)
command_builder.add_arg("--iidfile", iidfile) command_builder.add_arg("--target", target)
args = command_builder.build([path]) command_builder.add_arg("--iidfile", iidfile)
args = command_builder.build([path])
magic_word = "Successfully built " magic_word = "Successfully built "
appear = False appear = False
with subprocess.Popen(args, stdout=subprocess.PIPE, universal_newlines=True) as p: with subprocess.Popen(args, stdout=subprocess.PIPE, universal_newlines=True) as p:
while True: while True:
line = p.stdout.readline() line = p.stdout.readline()
if not line: if not line:
break break
if line.startswith(magic_word): if line.startswith(magic_word):
appear = True appear = True
yield json.dumps({"stream": line}) yield json.dumps({"stream": line})
with open(iidfile) as f: with open(iidfile) as f:
line = f.readline() line = f.readline()
image_id = line.split(":")[1].strip() image_id = line.split(":")[1].strip()
os.remove(iidfile) os.remove(iidfile)
if not appear: if not appear:
yield json.dumps({"stream": "{}{}\n".format(magic_word, image_id)}) yield json.dumps({"stream": "{}{}\n".format(magic_word, image_id)})
class _CommandBuilder(object): class _CommandBuilder(object):