diff --git a/renku_notebooks/__init__.py b/renku_notebooks/__init__.py index 42ff5de4c..18aa2a29a 100644 --- a/renku_notebooks/__init__.py +++ b/renku_notebooks/__init__.py @@ -36,6 +36,7 @@ VersionResponse, ) from .api.notebooks import ( + check_docker_image, user_servers, user_server, launch_notebook, @@ -160,6 +161,7 @@ def register_swagger(app): spec.path(view=server_logs) spec.path(view=autosave_info) spec.path(view=delete_autosave) + spec.path(view=check_docker_image) # Register security scheme security_scheme = { "type": "openIdConnect", diff --git a/renku_notebooks/api/notebooks.py b/renku_notebooks/api/notebooks.py index 403effa63..b0dcae4a9 100644 --- a/renku_notebooks/api/notebooks.py +++ b/renku_notebooks/api/notebooks.py @@ -23,6 +23,12 @@ from webargs import fields from webargs.flaskparser import use_args +from renku_notebooks.util.check_image import ( + get_docker_token, + image_exists, + parse_image_name, +) + from .. import config from .auth import authenticated from .schemas import ( @@ -462,3 +468,47 @@ def delete_autosave(user, namespace_project, autosave_name): ) autosave.delete() return make_response("", 204) + + +@bp.route("images", methods=["GET"]) +@use_args({"image_url": fields.String(required=True)}, as_kwargs=True, location="query") +@authenticated +def check_docker_image(user, image_url): + """ + Return the availability of the docker image. + + --- + get: + description: Docker image availability. + parameters: + - in: query + schema: + type: string + required: true + name: image_url + description: The Docker image URL (tag included) that should be fetched. + responses: + 200: + description: The Docker image is available. + 404: + description: The Docker image is not available. + tags: + - images + """ + parsed_image = parse_image_name(image_url) + if parsed_image is None: + return ( + jsonify( + { + "message": f"The image {image_url} cannot be parsed, " + "ensure you are providing a valid Docker image name.", + } + ), + 422, + ) + token, _ = get_docker_token(**parsed_image, user=user) + image_exists_result = image_exists(**parsed_image, token=token) + if image_exists_result: + return "", 200 + else: + return "", 404 diff --git a/tests/integration/test_image_check_endpoint.py b/tests/integration/test_image_check_endpoint.py new file mode 100644 index 000000000..7551c8da8 --- /dev/null +++ b/tests/integration/test_image_check_endpoint.py @@ -0,0 +1,17 @@ +import pytest +import requests + + +@pytest.mark.parametrize( + "image_url,expected_status_code", + [("nginx", 200), (":::?-+^", 422), ("image/does_not_exist:9999999999999.99999.9999", 404)], +) +def test_image_check_endpoint( + image_url, + expected_status_code, + headers, + base_url, +): + url = f"{base_url}/images" + res = requests.get(url, headers=headers, params={"image_url": image_url}) + assert res.status_code == expected_status_code