Use CLI to push docker image

Preserves existing authentication information handling behavior:

1. If registry_credentials are present, they are used but not leaked
   on to existing ~/.docker/config
2. If registry_credentials are not present, they are not used
3. Regardless of registry_credentials being present, we still will use
   existing authentication info in DOCKER_CONFIG
pull/1421/head
YuviPanda 2025-02-26 11:04:19 -08:00
rodzic 17a23c4ae6
commit ac0def4028
1 zmienionych plików z 41 dodań i 11 usunięć

Wyświetl plik

@ -2,9 +2,13 @@
Docker container engine for repo2docker Docker container engine for repo2docker
""" """
import os
import shutil import shutil
import subprocess
import tarfile import tarfile
import tempfile import tempfile
from contextlib import contextmanager
from pathlib import Path
from iso8601 import parse_date from iso8601 import parse_date
from traitlets import Dict, List, Unicode from traitlets import Dict, List, Unicode
@ -58,7 +62,7 @@ class DockerEngine(ContainerEngine):
https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build
""" """
string_output = False string_output = True
extra_init_args = Dict( extra_init_args = Dict(
{}, {},
@ -141,18 +145,12 @@ class DockerEngine(ContainerEngine):
args += [d] args += [d]
for line in execute_cmd(args, True): yield from execute_cmd(args, True)
# Simulate structured JSON output from buildx build, since we
# do get structured json output from pushing and running
yield {"stream": line}
else: else:
# Assume 'path' is passed in # Assume 'path' is passed in
args += [path] args += [path]
for line in execute_cmd(args, True): yield from execute_cmd(args, True)
# Simulate structured JSON output from buildx build, since we
# do get structured json output from pushing and running
yield {"stream": line}
def images(self): def images(self):
images = self._apiclient.images() images = self._apiclient.images()
@ -162,10 +160,42 @@ class DockerEngine(ContainerEngine):
image = self._apiclient.inspect_image(image) image = self._apiclient.inspect_image(image)
return Image(tags=image["RepoTags"], config=image["Config"]) return Image(tags=image["RepoTags"], config=image["Config"])
@contextmanager
def docker_login(self, username, password, registry):
# Determine existing DOCKER_CONFIG
dc_path = Path(
os.environ.get("DOCKER_CONFIG", os.path.expanduser("~/.docker/config.json"))
)
with tempfile.TemporaryDirectory() as d:
new_dc_path = Path(d) / "config.json"
if dc_path.exists():
# If there is an existing DOCKER_CONFIG, copy it to new location so we inherit
# whatever configuration the user has already set
shutil.copy2(dc_path, new_dc_path)
env = os.environ.copy()
subprocess.check_call(
# FIXME: This should be using --password-stdin instead
[
"docker",
"login",
"--username",
username,
"--password",
password,
registry,
],
env=env,
)
yield
def push(self, image_spec): def push(self, image_spec):
if self.registry_credentials: if self.registry_credentials:
self._apiclient.login(**self.registry_credentials) with self.docker_login(**self.registry_credentials):
return self._apiclient.push(image_spec, stream=True) yield from execute_cmd(["docker", "push", image_spec], capture=True)
else:
yield from execute_cmd(["docker", "push", image_spec], capture=True)
def run( def run(
self, self,