Skip to content

Commit

Permalink
Add a docker registry integration test with real registry
Browse files Browse the repository at this point in the history
  • Loading branch information
yuvipanda committed Feb 28, 2025
1 parent 364a189 commit 82e049f
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 66 deletions.
14 changes: 1 addition & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
- unit
- venv
- contentproviders
- registry
# Playwright test
- ui
include:
Expand All @@ -73,22 +74,9 @@ jobs:
python_version: "3.9"
repo_type: venv

services:
# So that we can test this in PRs/branches
local-registry:
image: registry:2
ports:
- 5000:5000

steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
# Allows pushing to registry on localhost:5000
driver-opts: network=host

- uses: actions/setup-python@v5
with:
python-version: "${{ matrix.python_version }}"
Expand Down
15 changes: 2 additions & 13 deletions repo2docker/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
RBuildPack,
)
from .engine import BuildError, ContainerEngineException, ImageLoadError
from .utils import ByteSpecification, R2dState, chdir, get_platform
from .utils import ByteSpecification, R2dState, chdir, get_platform, get_free_port


class Repo2Docker(Application):
Expand Down Expand Up @@ -660,7 +660,7 @@ def start_container(self):
container_port = int(container_port_proto.split("/", 1)[0])
else:
# no port specified, pick a random one
container_port = host_port = str(self._get_free_port())
container_port = host_port = str(get_free_port())
self.ports = {f"{container_port}/tcp": host_port}
self.port = host_port
# To use the option --NotebookApp.custom_display_url
Expand Down Expand Up @@ -744,17 +744,6 @@ def wait_for_container(self, container):
if exit_code:
sys.exit(exit_code)

def _get_free_port(self):
"""
Hacky method to get a free random port on local host
"""
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 0))
port = s.getsockname()[1]
s.close()
return port

def find_image(self):
# if this is a dry run it is Ok for dockerd to be unreachable so we
Expand Down
12 changes: 12 additions & 0 deletions repo2docker/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import socket
import platform
import re
import subprocess
Expand Down Expand Up @@ -545,3 +546,14 @@ def get_platform():
else:
warnings.warn(f"Unexpected platform '{m}', defaulting to linux/amd64")
return "linux/amd64"


def get_free_port():
"""
Hacky method to get a free random port on local host
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 0))
port = s.getsockname()[1]
s.close()
return port
2 changes: 2 additions & 0 deletions tests/registry/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Smallest possible dockerfile, used only for building images to be tested
FROM scratch
50 changes: 50 additions & 0 deletions tests/registry/test_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from pathlib import Path
import subprocess
import pytest
from repo2docker.__main__ import make_r2d
from repo2docker.utils import get_free_port
import time
import requests
import secrets

HERE = Path(__file__).parent

@pytest.fixture
def registry():
port = get_free_port()
cmd = [
"docker", "run", "-it", "-p", f"{port}:5000", "registry:3.0.0-rc.3"
]
proc = subprocess.Popen(cmd)
health_url = f'http://localhost:{port}/v2'
# Wait for the registry to actually come up
for i in range(10):
try:
resp = requests.get(health_url)
if resp.status_code in (401, 200):
break
except requests.ConnectionError:
# The service is not up yet
pass
time.sleep(i)
else:
raise TimeoutError("Test registry did not come up in time")

try:
yield f"localhost:{port}"
finally:
proc.terminate()
proc.wait()


def test_registry(registry):
image_name = f"{registry}/{secrets.token_hex(8)}:latest"
r2d = make_r2d([
"--image", image_name,
"--push", "--no-run", str(HERE)
])

r2d.start()

proc = subprocess.run(["docker", "manifest", "inspect", "--insecure", image_name])
assert proc.returncode == 0
40 changes: 0 additions & 40 deletions tests/unit/test_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,43 +22,3 @@ 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)

0 comments on commit 82e049f

Please sign in to comment.