Add registry optional credentials to push()

pull/1245/head
Simon Li 2023-02-12 00:16:14 +00:00
rodzic 1e61d4f1ae
commit 5e8ee1ca7d
3 zmienionych plików z 82 dodań i 0 usunięć

Wyświetl plik

@ -120,6 +120,8 @@ class DockerEngine(ContainerEngine):
return Image(tags=image["RepoTags"], config=image["ContainerConfig"]) return Image(tags=image["RepoTags"], config=image["ContainerConfig"])
def push(self, image_spec): def push(self, image_spec):
if self.registry_credentials:
self._apiclient.login(**self.registry_credentials)
return self._apiclient.push(image_spec, stream=True) return self._apiclient.push(image_spec, stream=True)
def run( def run(

Wyświetl plik

@ -2,8 +2,11 @@
Interface for a repo2docker container engine Interface for a repo2docker container engine
""" """
import json
import os
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from traitlets import Dict, default
from traitlets.config import LoggingConfigurable from traitlets.config import LoggingConfigurable
# Based on https://docker-py.readthedocs.io/en/4.2.0/containers.html # Based on https://docker-py.readthedocs.io/en/4.2.0/containers.html
@ -142,6 +145,37 @@ class ContainerEngine(LoggingConfigurable):
Initialised with a reference to the parent so can also be configured using traitlets. Initialised with a reference to the parent so can also be configured using traitlets.
""" """
registry_credentials = Dict(
help="""
Credentials dictionary, if set will be used to authenticate with
the registry. Typically this will include the keys:
- `username`: The registry username
- `password`: The registry password or token
- `registry`: The registry URL
This can also be set by passing a JSON object in the
CONTAINER_ENGINE_REGISTRY_CREDENTIALS environment variable.
""",
config=True,
)
@default("registry_credentials")
def _registry_credentials_default(self):
"""
Set the registry credentials from CONTAINER_ENGINE_REGISTRY_CREDENTIALS
"""
obj = os.getenv("CONTAINER_ENGINE_REGISTRY_CREDENTIALS")
if obj:
try:
return json.loads(obj)
except json.JSONDecodeError:
self.log.error(
"CONTAINER_ENGINE_REGISTRY_CREDENTIALS is not valid JSON"
)
raise
return {}
string_output = True string_output = True
""" """
Whether progress events should be strings or an object. Whether progress events should be strings or an object.
@ -251,6 +285,9 @@ class ContainerEngine(LoggingConfigurable):
""" """
Push image to a registry Push image to a registry
If the registry_credentials traitlets is set it should be used to
authenticate with the registry before pushing.
Parameters Parameters
---------- ----------
image_spec : str image_spec : str

Wyświetl plik

@ -2,6 +2,9 @@
import os import os
from subprocess import check_output from subprocess import check_output
from unittest.mock import Mock, patch
from repo2docker.docker import DockerEngine
repo_root = os.path.abspath( repo_root = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
@ -19,3 +22,43 @@ def test_git_credential_env():
.strip() .strip()
) )
assert out == credential_env assert out == credential_env
class MockDockerEngine(DockerEngine):
def __init__(self, *args, **kwargs):
self._apiclient = Mock()
def test_docker_push_no_credentials():
engine = MockDockerEngine()
engine.push("image")
assert len(engine._apiclient.method_calls) == 1
engine._apiclient.push.assert_called_once_with("image", stream=True)
def test_docker_push_dict_credentials():
engine = MockDockerEngine()
engine.registry_credentials = {"username": "abc", "password": "def"}
engine.push("image")
assert len(engine._apiclient.method_calls) == 2
engine._apiclient.login.assert_called_once_with(username="abc", password="def")
engine._apiclient.push.assert_called_once_with("image", stream=True)
def test_docker_push_env_credentials():
engine = MockDockerEngine()
with patch.dict(
"os.environ",
{
"CONTAINER_ENGINE_REGISTRY_CREDENTIALS": '{"username": "abc", "password": "def"}'
},
):
engine.push("image")
assert len(engine._apiclient.method_calls) == 2
engine._apiclient.login.assert_called_once_with(username="abc", password="def")
engine._apiclient.push.assert_called_once_with("image", stream=True)