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"])
def push(self, image_spec):
if self.registry_credentials:
self._apiclient.login(**self.registry_credentials)
return self._apiclient.push(image_spec, stream=True)
def run(

Wyświetl plik

@ -2,8 +2,11 @@
Interface for a repo2docker container engine
"""
import json
import os
from abc import ABC, abstractmethod
from traitlets import Dict, default
from traitlets.config import LoggingConfigurable
# 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.
"""
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
"""
Whether progress events should be strings or an object.
@ -251,6 +285,9 @@ class ContainerEngine(LoggingConfigurable):
"""
Push image to a registry
If the registry_credentials traitlets is set it should be used to
authenticate with the registry before pushing.
Parameters
----------
image_spec : str

Wyświetl plik

@ -2,6 +2,9 @@
import os
from subprocess import check_output
from unittest.mock import Mock, patch
from repo2docker.docker import DockerEngine
repo_root = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
@ -19,3 +22,43 @@ def test_git_credential_env():
.strip()
)
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)