From 07e97ee5ceff625236f8ad5e0b4b2ca3e89be9cc Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Fri, 28 Feb 2025 14:16:28 -0800 Subject: [PATCH] Turn push and load into params for the build arg --- repo2docker/app.py | 60 +++++-------------------------------------- repo2docker/docker.py | 45 ++++++++++++++++++-------------- repo2docker/engine.py | 6 +++++ 3 files changed, 39 insertions(+), 72 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 54482b7a..816fc0bf 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -572,55 +572,6 @@ def initialize(self, *args, **kwargs): if self.volumes and not self.run: raise ValueError("Cannot mount volumes if container is not run") - def push_image(self): - """Push docker image to registry""" - client = self.get_engine() - # Build a progress setup for each layer, and only emit per-layer - # info every 1.5s - progress_layers = {} - layers = {} - last_emit_time = time.time() - for chunk in client.push(self.output_image_spec): - if client.string_output: - self.log.info(chunk, extra=dict(phase=R2dState.PUSHING)) - continue - # else this is Docker output - - # each chunk can be one or more lines of json events - # split lines here in case multiple are delivered at once - for line in chunk.splitlines(): - line = line.decode("utf-8", errors="replace") - try: - progress = json.loads(line) - except Exception as e: - self.log.warning("Not a JSON progress line: %r", line) - continue - if "error" in progress: - self.log.error(progress["error"], extra=dict(phase=R2dState.FAILED)) - raise ImageLoadError(progress["error"]) - if "id" not in progress: - continue - # deprecated truncated-progress data - if "progressDetail" in progress and progress["progressDetail"]: - progress_layers[progress["id"]] = progress["progressDetail"] - else: - progress_layers[progress["id"]] = progress["status"] - # include full progress data for each layer in 'layers' data - layers[progress["id"]] = progress - if time.time() - last_emit_time > 1.5: - self.log.info( - "Pushing image\n", - extra=dict( - progress=progress_layers, - layers=layers, - phase=R2dState.PUSHING, - ), - ) - last_emit_time = time.time() - self.log.info( - f"Successfully pushed {self.output_image_spec}", - extra=dict(phase=R2dState.PUSHING), - ) def run_image(self): """Run docker container from built image @@ -847,6 +798,12 @@ def build(self): extra=dict(phase=R2dState.BUILDING), ) + extra_build_kwargs = self.extra_build_kwargs.copy() + # Set "push" and "load" parameters in a backwards compat way, without + # having to change the signature of every buildpack + extra_build_kwargs["push"] = self.push + extra_build_kwargs["load"] = self.run + for l in picked_buildpack.build( docker_client, self.output_image_spec, @@ -854,7 +811,7 @@ def build(self): self.build_memory_limit, build_args, self.cache_from, - self.extra_build_kwargs, + extra_build_kwargs, platform=self.platform, ): if docker_client.string_output: @@ -886,8 +843,5 @@ def build(self): def start(self): self.build() - if self.push: - self.push_image() - if self.run: self.run_image() diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 1cb55fd0..42001448 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -2,13 +2,14 @@ Docker container engine for repo2docker """ +from argparse import ArgumentError import json import os import shutil import subprocess import tarfile import tempfile -from contextlib import contextmanager +from contextlib import ExitStack, contextmanager from pathlib import Path from iso8601 import parse_date @@ -89,6 +90,8 @@ class DockerEngine(ContainerEngine): def build( self, + push=False, + load=False, *, buildargs=None, cache_from=None, @@ -104,7 +107,15 @@ def build( ): if not shutil.which("docker"): raise RuntimeError("The docker commandline client must be installed") - args = ["docker", "buildx", "build", "--progress", "plain", "--load"] + args = ["docker", "buildx", "build", "--progress", "plain"] + if load: + if push: + raise ValueError("Setting push=True and load=True is currently not supported") + args.append("--load") + + if push: + args.append("--push") + if buildargs: for k, v in buildargs.items(): args += ["--build-arg", f"{k}={v}"] @@ -129,19 +140,22 @@ def build( # place extra args right *before* the path args += self.extra_buildx_build_args - if fileobj: - with tempfile.TemporaryDirectory() as d: - tarf = tarfile.open(fileobj=fileobj) - tarf.extractall(d) + with ExitStack() as stack: + if self.registry_credentials: + stack.enter_context(self.docker_login(**self.registry_credentials)) + if fileobj: + with tempfile.TemporaryDirectory() as d: + tarf = tarfile.open(fileobj=fileobj) + tarf.extractall(d) - args += [d] + args += [d] - yield from execute_cmd(args, True) - else: - # Assume 'path' is passed in - args += [path] + yield from execute_cmd(args, True) + else: + # Assume 'path' is passed in + args += [path] - yield from execute_cmd(args, True) + yield from execute_cmd(args, True) def inspect_image(self, image): """ @@ -194,13 +208,6 @@ def docker_login(self, username, password, registry): else: del os.environ["DOCKER_CONFIG"] - def push(self, image_spec): - if self.registry_credentials: - with self.docker_login(**self.registry_credentials): - yield from execute_cmd(["docker", "push", image_spec], capture=True) - else: - yield from execute_cmd(["docker", "push", image_spec], capture=True) - def run( self, image_spec, diff --git a/repo2docker/engine.py b/repo2docker/engine.py index d081a33c..28580ebd 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -212,6 +212,8 @@ def __init__(self, *, parent): def build( self, + push=False, + load=False, *, buildargs={}, cache_from=[], @@ -230,6 +232,10 @@ def build( Parameters ---------- + push: bool + Push the resulting image to a registry + load: bool + Load the resulting image into the container store ready to be run buildargs : dict Dictionary of build arguments cache_from : list[str]