diff --git a/ros_cross_compile/docker_client.py b/ros_cross_compile/docker_client.py index c9045f64..513e399f 100644 --- a/ros_cross_compile/docker_client.py +++ b/ros_cross_compile/docker_client.py @@ -19,8 +19,7 @@ from typing import Optional import docker -from tqdm import tqdm - +from docker.utils import kwargs_from_env as docker_kwargs_from_env logging.basicConfig(level=logging.INFO) logger = logging.getLogger('Docker Client') @@ -28,6 +27,25 @@ DEFAULT_COLCON_DEFAULTS_FILE = 'defaults.yaml' +class GeneratorStream(io.RawIOBase): + def __init__(self, generator): + self.leftover = None + self.generator = generator + + def readable(self): + return True + + def readinto(self, b): + try: + length = len(b) # : We're supposed to return at most this much + chunk = self.leftover or next(self.generator) + output, self.leftover = chunk[:length], chunk[length:] + b[:len(output)] = output + return len(output) + except StopIteration: + return 0 # : Indicate EOF + + class DockerClient: """Simplified Docker API for this package's usage patterns.""" @@ -66,7 +84,8 @@ def build_image( :raises docker.errors.BuildError: on build error """ # Use low-level API to expose logs for image building - docker_api = docker.APIClient(base_url='unix://var/run/docker.sock') + docker_api = docker.APIClient(**docker_kwargs_from_env()) + logger.info('Sending context to Docker client') log_generator = docker_api.build( path=str(dockerfile_dir) if dockerfile_dir else self._default_docker_dir, dockerfile=dockerfile_name, @@ -89,15 +108,10 @@ def _process_build_log(self, log_generator) -> None: raise docker.errors.BuildError(error_line) line = chunk.get('stream', '') line = line.rstrip() - if not line: - continue - # if line.startswith('Step '): - # # e.g. "Step X/Y" - # progress = line.split()[1] - # current, total = progress.split('/') - # current = int(current) - # total = int(total) - logger.info(line) + if line: + logger.info(line) + + def run_container( self, @@ -159,8 +173,7 @@ def get_image_size(self, img_name: str) -> int: def export_image_filesystem(self, image_tag: str): container = self._client.containers.run(image=image_tag, detach=True) - export_response = container.export() - tar_bytes = export_response.read() - tar_file = io.BytesIO(tar_bytes) - tar = tarfile.open(fileobj=tar_file) + export_generator = container.export() + stream = io.BufferedReader(GeneratorStream(export_generator)) + tar = tarfile.open(fileobj=stream, mode='r|*') return tar diff --git a/ros_cross_compile/ros_cross_compile.py b/ros_cross_compile/ros_cross_compile.py index f11fa91d..e4d6763a 100644 --- a/ros_cross_compile/ros_cross_compile.py +++ b/ros_cross_compile/ros_cross_compile.py @@ -159,6 +159,7 @@ def parse_args(args: List[str]) -> argparse.Namespace: '--skip-steps', nargs='+', choices=[stage.name for stage in stages], + default=[], help='Skip these steps') parser.add_argument( '--create-runtime-image', diff --git a/setup.py b/setup.py index 7e1fd6bb..aa446a04 100644 --- a/setup.py +++ b/setup.py @@ -51,9 +51,8 @@ package_name: ['docker/*.*', 'mixins/*.*'], }, install_requires=[ - 'docker>=2,<3', + 'docker==4.*', 'setuptools', - 'tqdm', ], zip_safe=True, entry_points={