コード例 #1
0
 def test_get_registry_hostname__bad_url_empty_hostname(
         self, registry_url: str) -> None:
     with pytest.raises(ValueError, match="Empty hostname in registry URL"):
         _ImageNameParser(
             default_user="******",
             default_cluster="test-cluster",
             registry_url=URL(registry_url),
         )
コード例 #2
0
 def test_parse_as_neuro_image__registry_has_port__image_in_bad_repo(
         self) -> None:
     my_parser = _ImageNameParser(default_user="******",
                                  registry_url=URL("http://localhost:5000"))
     image = "localhost:9999/bob/library/ubuntu:v10.04"
     with pytest.raises(ValueError, match="scheme 'image://' is required"):
         my_parser.parse_as_neuro_image(image)
コード例 #3
0
 def test_get_registry_hostname(self, registry_url: str) -> None:
     parser = _ImageNameParser(
         default_user="******",
         default_cluster="test-cluster",
         registry_url=URL(registry_url),
     )
     assert parser._registry == "reg.neu.ro"
コード例 #4
0
 def convert(self, value: str, param: Optional[click.Parameter],
             ctx: Optional[click.Context]) -> RemoteImage:
     assert ctx is not None
     root = cast(Root, ctx.obj)
     config = Factory(root.config_path)._read()
     image_parser = _ImageNameParser(config.auth_token.username,
                                     config.cluster_config.registry_url)
     return image_parser.parse_as_neuro_image(value, allow_tag=False)
コード例 #5
0
 def test_parse_remote__registry_has_port__image_in_good_repo(self) -> None:
     my_parser = _ImageNameParser(default_user="******",
                                  registry_url=URL("http://localhost:5000"))
     image = "localhost:5000/bob/library/ubuntu:v10.04"
     parsed = my_parser.parse_remote(image)
     assert parsed == RemoteImage(name="library/ubuntu",
                                  tag="v10.04",
                                  owner="bob",
                                  registry="localhost:5000")
コード例 #6
0
 def test_is_in_neuro_registry__registry_has_port(self) -> None:
     my_parser = _ImageNameParser(
         default_user="******",
         default_cluster="test-cluster",
         registry_url=URL("http://localhost:5000"),
     )
     image = "ubuntu:v10.04"
     parsed = my_parser.parse_as_local_image(image)
     assert parsed == LocalImage(name="ubuntu", tag="v10.04")
コード例 #7
0
 def test_is_in_neuro_registry__registry_has_port__local_image(
         self) -> None:
     my_parser = _ImageNameParser(
         default_user="******",
         default_cluster="test-cluster",
         registry_url=URL("http://localhost:5000"),
     )
     image = "ubuntu:v10.04"
     assert my_parser.is_in_neuro_registry(image) is False
コード例 #8
0
 def test_is_in_neuro_registry__registry_has_port__image_in_good_repo(
         self) -> None:
     my_parser = _ImageNameParser(
         default_user="******",
         default_cluster="test-cluster",
         registry_url=URL("http://localhost:5000"),
     )
     image = "localhost:5000/bob/library/ubuntu:v10.04"
     assert my_parser.is_in_neuro_registry(image) is True
コード例 #9
0
def parse_resource_for_sharing(uri: str, root: Root) -> URL:
    """ Parses the neuromation resource URI string.
    Available schemes: storage, image, job. For image URIs, tags are not allowed.
    """
    if uri.startswith("image:"):
        parser = _ImageNameParser(root.username, root.registry_url)
        image = parser.parse_as_neuro_image(uri, allow_tag=False)
        uri = str(image)

    return uri_from_cli(uri,
                        root.username,
                        allowed_schemes=("storage", "image", "job"))
コード例 #10
0
 def convert(self, value: str, param: Optional[click.Parameter],
             ctx: Optional[click.Context]) -> LocalImage:
     assert ctx is not None
     root = cast(Root, ctx.obj)
     config = Factory(root.config_path)._read()
     image_parser = _ImageNameParser(config.auth_token.username,
                                     config.cluster_config.registry_url)
     if image_parser.is_in_neuro_registry(value):
         raise click.BadParameter("remote image cannot be used as local",
                                  ctx, param, self.name)
     else:
         parsed_image = image_parser.parse_as_local_image(value)
     return parsed_image
コード例 #11
0
 def test_parse_remote__registry_has_port__image_in_other_repo(
         self) -> None:
     my_parser = _ImageNameParser(default_user="******",
                                  registry_url=URL("http://localhost:5000"))
     image = "example.com:9999/bob/library/ubuntu:v10.04"
     parsed = my_parser.parse_remote(image)
     # NOTE: "owner" is parsed only for images in neuromation registry
     assert parsed == RemoteImage(
         name="bob/library/ubuntu",
         tag="v10.04",
         owner=None,
         registry="example.com:9999",
     )
コード例 #12
0
 def test_parse_remote__registry_has_port__neuro_image(self) -> None:
     my_parser = _ImageNameParser(
         default_user="******",
         default_cluster="test-cluster",
         registry_url=URL("http://localhost:5000"),
     )
     image = "image://test-cluster/bob/library/ubuntu:v10.04"
     parsed = my_parser.parse_remote(image)
     assert parsed == RemoteImage.new_neuro_image(
         name="library/ubuntu",
         tag="v10.04",
         owner="bob",
         cluster_name="test-cluster",
         registry="localhost:5000",
     )
コード例 #13
0
 def test_get_registry_hostname_with_port(self, registry_url: str) -> None:
     parser = _ImageNameParser(default_user="******",
                               registry_url=URL(registry_url))
     assert parser._registry == "reg.neu.ro:5000"
コード例 #14
0
class TestImages:
    parser = _ImageNameParser(default_user="******",
                              registry_url=URL("https://registry-dev.neu.ro"))

    @asynctest.mock.patch(
        "aiodocker.Docker.__init__",
        side_effect=ValueError(
            "text Either DOCKER_HOST or local sockets are not available text"),
    )
    async def test_unavailable_docker(self, patched_init: Any,
                                      make_client: _MakeClient) -> None:
        image = self.parser.parse_as_neuro_image("image://bob/image:bananas")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            with pytest.raises(DockerError,
                               match=r"Docker engine is not available.+"):
                await client.images.pull(image, local_image)

    @asynctest.mock.patch("aiodocker.Docker.__init__",
                          side_effect=ValueError("something went wrong"))
    async def test_unknown_docker_error(self, patched_init: Any,
                                        make_client: _MakeClient) -> None:
        image = self.parser.parse_as_neuro_image("image://bob/image:bananas")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            with pytest.raises(ValueError, match=r"something went wrong"):
                await client.images.pull(image, local_image)

    @asynctest.mock.patch("aiodocker.images.DockerImages.tag")
    async def test_push_non_existent_image(self, patched_tag: Any,
                                           make_client: _MakeClient) -> None:
        patched_tag.side_effect = DockerError(404, {"message": "Mocked error"})
        image = self.parser.parse_as_neuro_image(
            "image://bob/image:bananas-no-more")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            with pytest.raises(ValueError, match=r"not found"):
                await client.images.push(local_image, image)

    @asynctest.mock.patch("aiodocker.images.DockerImages.tag")
    @asynctest.mock.patch("aiodocker.images.DockerImages.push")
    async def test_push_image_to_foreign_repo(
            self, patched_push: Any, patched_tag: Any,
            make_client: _MakeClient) -> None:
        patched_tag.return_value = True
        patched_push.side_effect = DockerError(403,
                                               {"message": "Mocked error"})
        image = self.parser.parse_as_neuro_image(
            "image://bob/image:bananas-no-more")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            with pytest.raises(AuthorizationError):
                await client.images.push(local_image, image)

    @asynctest.mock.patch("aiodocker.images.DockerImages.tag")
    @asynctest.mock.patch("aiodocker.images.DockerImages.push")
    async def test_push_image_with_docker_api_error(
            self, patched_push: Any, patched_tag: Any,
            make_client: _MakeClient) -> None:
        async def error_generator() -> AsyncIterator[Dict[str, Any]]:
            yield {"error": True, "errorDetail": {"message": "Mocked message"}}

        patched_tag.return_value = True
        patched_push.return_value = error_generator()
        image = self.parser.parse_as_neuro_image(
            "image://bob/image:bananas-wrong-food")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            with pytest.raises(DockerError) as exc_info:
                await client.images.push(local_image, image)
        assert exc_info.value.status == 900
        assert exc_info.value.message == "Mocked message"

    @asynctest.mock.patch("aiodocker.images.DockerImages.tag")
    @asynctest.mock.patch("aiodocker.images.DockerImages.push")
    async def test_success_push_image(self, patched_push: Any,
                                      patched_tag: Any,
                                      make_client: _MakeClient) -> None:
        async def message_generator() -> AsyncIterator[Dict[str, Any]]:
            yield {}

        patched_tag.return_value = True
        patched_push.return_value = message_generator()
        image = self.parser.parse_as_neuro_image(
            "image://bob/image:bananas-is-here")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            result = await client.images.push(local_image, image)
        assert result == image

    @asynctest.mock.patch("aiodocker.images.DockerImages.tag")
    @asynctest.mock.patch("aiodocker.images.DockerImages.push")
    async def test_success_push_image_no_target(
            self, patched_push: Any, patched_tag: Any,
            make_client: _MakeClient) -> None:
        async def message_generator() -> AsyncIterator[Dict[str, Any]]:
            yield {}

        patched_tag.return_value = True
        patched_push.return_value = message_generator()
        image = self.parser.parse_as_neuro_image("image://user/bananas:latest")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            result = await client.images.push(local_image)
        assert result == image

    @asynctest.mock.patch("aiodocker.images.DockerImages.pull")
    async def test_pull_non_existent_image(self, patched_pull: Any,
                                           make_client: _MakeClient) -> None:
        patched_pull.side_effect = DockerError(404,
                                               {"message": "Mocked error"})
        async with make_client("https://api.localhost.localdomain") as client:
            image = self.parser.parse_as_neuro_image(
                "image://bob/image:no-bananas-here")
            local_image = self.parser.parse_as_local_image("bananas:latest")
            with pytest.raises(ValueError, match=r"not found"):
                await client.images.pull(image, local_image)

    @asynctest.mock.patch("aiodocker.images.DockerImages.pull")
    async def test_pull_image_from_foreign_repo(
            self, patched_pull: Any, make_client: _MakeClient) -> None:
        patched_pull.side_effect = DockerError(403,
                                               {"message": "Mocked error"})
        image = self.parser.parse_as_neuro_image(
            "image://bob/image:not-your-bananas")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            with pytest.raises(AuthorizationError):
                await client.images.pull(image, local_image)

    @asynctest.mock.patch("aiodocker.images.DockerImages.pull")
    async def test_pull_image_with_docker_api_error(self, patched_pull: Any,
                                                    make_client: Any) -> None:
        async def error_generator() -> AsyncIterator[Dict[str, Any]]:
            yield {"error": True, "errorDetail": {"message": "Mocked message"}}

        patched_pull.return_value = error_generator()
        image = self.parser.parse_as_neuro_image("image://bob/image:nuts-here")
        async with make_client("https://api.localhost.localdomain") as client:
            with pytest.raises(DockerError) as exc_info:
                await client.images.pull(image, image)
        assert exc_info.value.status == 900
        assert exc_info.value.message == "Mocked message"

    @asynctest.mock.patch("aiodocker.images.DockerImages.tag")
    @asynctest.mock.patch("aiodocker.images.DockerImages.pull")
    async def test_success_pull_image(self, patched_pull: Any,
                                      patched_tag: Any,
                                      make_client: _MakeClient) -> None:
        async def message_generator() -> AsyncIterator[Dict[str, Any]]:
            yield {}

        patched_tag.return_value = True
        patched_pull.return_value = message_generator()
        image = self.parser.parse_as_neuro_image("image://bob/image:bananas")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            result = await client.images.pull(image, local_image)
        assert result == local_image

    @asynctest.mock.patch("aiodocker.images.DockerImages.tag")
    @asynctest.mock.patch("aiodocker.images.DockerImages.pull")
    async def test_success_pull_image_no_target(
            self, patched_pull: Any, patched_tag: Any,
            make_client: _MakeClient) -> None:
        async def message_generator() -> AsyncIterator[Dict[str, Any]]:
            yield {}

        patched_tag.return_value = True
        patched_pull.return_value = message_generator()
        image = self.parser.parse_as_neuro_image("image://bob/bananas:latest")
        local_image = self.parser.parse_as_local_image("bananas:latest")
        async with make_client("https://api.localhost.localdomain") as client:
            result = await client.images.pull(image)
        assert result == local_image
コード例 #15
0
 def test_is_in_neuro_registry__registry_has_port__image_in_bad_repo(
         self) -> None:
     my_parser = _ImageNameParser(default_user="******",
                                  registry_url=URL("http://localhost:5000"))
     image = "localhost:9999/bob/library/ubuntu:v10.04"
     assert my_parser.is_in_neuro_registry(image) is False
コード例 #16
0
class TestImageParser:
    parser = _ImageNameParser(default_user="******",
                              registry_url=URL("https://reg.neu.ro"))

    @pytest.mark.parametrize(
        "image",
        [
            "image://me/ubuntu:v10.04",
            "image://~/ubuntu:v10.04",
            "image:///ubuntu:v10.04",
            "image:ubuntu:v10.04",
            "ubuntu:v10.04",
        ],
    )
    def test_has_tag_ok(self, image: str) -> None:
        assert self.parser.has_tag(image)

    def test_has_tag_no_tag(self) -> None:
        image = "ubuntu"
        assert not self.parser.has_tag(image)

    def test_has_tag_no_tag_with_slash(self) -> None:
        image = "library/ubuntu"
        assert not self.parser.has_tag(image)

    def test_has_tag_empty_tag(self) -> None:
        image = "ubuntu:"
        with pytest.raises(ValueError, match="empty tag"):
            self.parser.has_tag(image)

    def test_has_tag_empty_tag_with_slash(self) -> None:
        image = "library/ubuntu:"
        with pytest.raises(ValueError, match="empty tag"):
            self.parser.has_tag(image)

    def test_has_tag_empty_image_name(self) -> None:
        image = ":latest"
        with pytest.raises(ValueError, match="empty name"):
            self.parser.has_tag(image)

    def test_has_tag_too_many_tags(self) -> None:
        image = "ubuntu:v10.04:latest"
        with pytest.raises(ValueError, match="too many tags"):
            self.parser.has_tag(image)

    @pytest.mark.parametrize(
        "registry_url",
        [
            "http://reg.neu.ro", "https://reg.neu.ro",
            "https://reg.neu.ro/bla/bla"
        ],
    )
    def test_get_registry_hostname(self, registry_url: str) -> None:
        parser = _ImageNameParser(default_user="******",
                                  registry_url=URL(registry_url))
        assert parser._registry == "reg.neu.ro"

    @pytest.mark.parametrize(
        "registry_url",
        ["http://reg.neu.ro:5000", "http://reg.neu.ro:5000/bla/bla"])
    def test_get_registry_hostname_with_port(self, registry_url: str) -> None:
        parser = _ImageNameParser(default_user="******",
                                  registry_url=URL(registry_url))
        assert parser._registry == "reg.neu.ro:5000"

    @pytest.mark.parametrize(
        "registry_url",
        ["", "reg.neu.ro", "reg.neu.ro:5000", "https://", "https:///bla/bla"],
    )
    def test_get_registry_hostname__bad_url_empty_hostname(
            self, registry_url: str) -> None:
        with pytest.raises(ValueError, match="Empty hostname in registry URL"):
            _ImageNameParser(default_user="******",
                             registry_url=URL(registry_url))

    def test_split_image_name_no_tag(self) -> None:
        splitted = self.parser._split_image_name("ubuntu",
                                                 self.parser.default_tag)
        assert splitted == ("ubuntu", "latest")

    def test_split_image_name_with_tag(self) -> None:
        splitted = self.parser._split_image_name("ubuntu:v10.04",
                                                 self.parser.default_tag)
        assert splitted == ("ubuntu", "v10.04")

    def test_split_image_name_empty_tag(self) -> None:
        with pytest.raises(ValueError, match="empty tag"):
            self.parser._split_image_name("ubuntu:", self.parser.default_tag)

    def test_split_image_name_two_tags(self) -> None:
        with pytest.raises(ValueError, match="too many tags"):
            self.parser._split_image_name("ubuntu:v10.04:LTS",
                                          self.parser.default_tag)

    def test_split_image_name_with_registry_port_no_tag(self) -> None:
        splitted = self.parser._split_image_name("localhost:5000/ubuntu",
                                                 self.parser.default_tag)
        assert splitted == ("localhost:5000/ubuntu", "latest")

    def test_split_image_name_with_registry_port_with_tag(self) -> None:
        splitted = self.parser._split_image_name(
            "localhost:5000/ubuntu:v10.04", self.parser.default_tag)
        assert splitted == ("localhost:5000/ubuntu", "v10.04")

    def test_split_image_name_with_registry_port_two_tags(self) -> None:
        with pytest.raises(ValueError, match="too many tags"):
            self.parser._split_image_name("localhost:5000/ubuntu:v10.04:LTS",
                                          self.parser.default_tag)

    def test_split_image_name_with_registry_port_empty_tag(self) -> None:
        with pytest.raises(ValueError, match="empty tag"):
            self.parser._split_image_name("localhost:5000/ubuntu:",
                                          self.parser.default_tag)

    def test_split_image_name_with_registry_port_slash_in_tag(self) -> None:
        with pytest.raises(ValueError, match="invalid tag"):
            self.parser._split_image_name("localhost:5000/ubuntu:v10/04",
                                          self.parser.default_tag)

    # public method: parse_local

    @pytest.mark.parametrize(
        "url", ["image://", "image:///", "image://bob", "image://bob/"])
    def test_parse_as_neuro_image__no_image_name(self, url: str) -> None:
        with pytest.raises(ValueError, match="no image name specified"):
            self.parser.parse_as_neuro_image(url)

    def test_parse_as_local_image_empty_fail(self) -> None:
        image = ""
        with pytest.raises(ValueError, match="empty image name"):
            self.parser.parse_as_local_image(image)

    def test_parse_as_local_image_dash_fail(self) -> None:
        image = "-zxc"
        with pytest.raises(ValueError, match="image cannot start with dash"):
            self.parser.parse_as_local_image(image)

    def test_parse_as_local_image_with_image_scheme_fail(self) -> None:
        image = "image://ubuntu"
        with pytest.raises(
                ValueError,
                match="scheme 'image://' is not allowed for local images"):
            self.parser.parse_as_local_image(image)

    def test_parse_as_local_image_with_other_scheme_ok(self) -> None:
        image = "http://ubuntu"
        parsed = self.parser.parse_as_local_image(image)
        # instead of parser, the docker client will fail
        assert parsed == LocalImage(name="http://ubuntu", tag="latest")

    def test_parse_as_local_image_no_tag(self) -> None:
        image = "ubuntu"
        parsed = self.parser.parse_as_local_image(image)
        assert parsed == LocalImage(name="ubuntu", tag="latest")

    def test_parse_as_local_image_with_tag(self) -> None:
        image = "ubuntu:v10.04"
        parsed = self.parser.parse_as_local_image(image)
        assert parsed == LocalImage(name="ubuntu", tag="v10.04")

    def test_parse_as_local_image_2_tag_fail(self) -> None:
        image = "ubuntu:v10.04:LTS"
        with pytest.raises(ValueError, match="too many tags"):
            self.parser.parse_as_local_image(image)

    # public method: parse_remote

    @pytest.mark.parametrize(
        "url",
        [
            "image://bob/ubuntu:v10.04?key=value",
            "image://bob/ubuntu?key=value",
            "image:///ubuntu?key=value",
            "image:ubuntu?key=value",
        ],
    )
    def test_parse_as_neuro_image__with_query__fail(self, url: str) -> None:
        with pytest.raises(ValueError, match="query is not allowed"):
            self.parser.parse_as_neuro_image(url)

    @pytest.mark.parametrize(
        "url",
        [
            "image://bob/ubuntu:v10.04#fragment",
            "image://bob/ubuntu#fragment",
            "image:///ubuntu#fragment",
            "image:ubuntu#fragment",
        ],
    )
    def test_parse_as_neuro_image__with_fragment__fail(self, url: str) -> None:
        with pytest.raises(ValueError, match="fragment is not allowed"):
            self.parser.parse_as_neuro_image(url)

    def test_parse_as_neuro_image__with_user__fail(self) -> None:
        url = "image://user@bob/ubuntu"
        with pytest.raises(ValueError, match="user is not allowed"):
            self.parser.parse_as_neuro_image(url)

    def test_parse_as_neuro_image__with_password__fail(self) -> None:
        url = "image://:password@bob/ubuntu"
        with pytest.raises(ValueError, match="password is not allowed"):
            self.parser.parse_as_neuro_image(url)

    def test_parse_as_neuro_image__with_port__fail(self) -> None:
        url = "image://bob:443/ubuntu"
        with pytest.raises(ValueError, match="port is not allowed"):
            self.parser.parse_as_neuro_image(url)

    def test_parse_as_neuro_image_empty_fail__fail(self) -> None:
        image = ""
        with pytest.raises(ValueError, match="empty image name"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_dash_fail__fail(self) -> None:
        image = "-zxc"
        with pytest.raises(ValueError, match="image cannot start with dash"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_no_scheme_fail(self) -> None:
        image = "ubuntu"
        with pytest.raises(
                ValueError,
                match="scheme 'image://' is required for remote images"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_invalid_scheme_1_fail(self) -> None:
        image = "ubuntu:latest"
        with pytest.raises(
                ValueError,
                match="scheme 'image://' is required for remote images"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_invalid_scheme_2_fail(self) -> None:
        image = "http://ubuntu"
        with pytest.raises(
                ValueError,
                match="scheme 'image://' is required for remote images"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_with_scheme_with_user_with_tag(self) -> None:
        image = "image://bob/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="v10.04",
                                     owner="bob",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_with_user_with_tag_2(
            self) -> None:
        image = "image://bob/library/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="bob",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_with_user_no_tag(self) -> None:
        image = "image://bob/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="latest",
                                     owner="bob",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_with_user_no_tag_2(self) -> None:
        image = "image://bob/library/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="latest",
                                     owner="bob",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_no_slash_no_user_no_tag(
            self) -> None:
        image = "image:ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_no_slash_no_user_no_tag_2(
            self) -> None:
        image = "image:library/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_no_slash_no_user_with_tag(
            self) -> None:
        image = "image:ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_no_slash_no_user_with_tag_2(
            self) -> None:
        image = "image:library/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_1_slash_no_user_no_tag(
            self) -> None:
        image = "image:/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_1_slash_no_user_no_tag_2(
            self) -> None:
        image = "image:/library/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_1_slash_no_user_with_tag(
            self) -> None:
        image = "image:/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_1_slash_no_user_with_tag_2(
            self) -> None:
        image = "image:/library/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_2_slash_user_no_tag_fail(
            self) -> None:
        image = "image://ubuntu"
        with pytest.raises(ValueError, match="no image name specified"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_with_scheme_2_slash_user_with_tag_fail(
            self) -> None:
        image = "image://ubuntu:v10.04"
        with pytest.raises(ValueError,
                           match="port can't be converted to integer"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_with_scheme_3_slash_no_user_no_tag(
            self) -> None:
        image = "image:///ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_3_slash_no_user_no_tag_2(
            self) -> None:
        image = "image:///library/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_3_slash_no_user_with_tag(
            self) -> None:
        image = "image:///ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_3_slash_no_user_with_tag_2(
            self) -> None:
        image = "image:///library/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_4_slash_no_user_with_tag(
            self) -> None:
        image = "image:////ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_4_slash_no_user_with_tag_2(
            self) -> None:
        image = "image:////library/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_4_slash_no_user_no_tag(
            self) -> None:
        image = "image:////ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_4_slash_no_user_no_tag_2(
            self) -> None:
        image = "image:////library/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_tilde_user_no_tag(self) -> None:
        image = "image://~/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_tilde_user_no_tag_2(
            self) -> None:
        image = "image://~/library/ubuntu"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="latest",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_tilde_user_with_tag(
            self) -> None:
        image = "image://~/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_with_scheme_tilde_user_with_tag_2(
            self) -> None:
        image = "image://~/library/ubuntu:v10.04"
        parsed = self.parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_no_scheme_no_slash_no_tag_fail(self) -> None:
        image = "ubuntu"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_no_scheme_no_slash_with_tag_fail(
            self) -> None:
        image = "ubuntu:v10.04"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_no_scheme_1_slash_no_tag_fail(self) -> None:
        image = "library/ubuntu"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_no_scheme_1_slash_with_tag_fail(
            self) -> None:
        image = "library/ubuntu:v10.04"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_no_scheme_2_slash_no_tag_fail(self) -> None:
        image = "docker.io/library/ubuntu"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_no_scheme_2_slash_with_tag_fail(
            self) -> None:
        image = "docker.io/library/ubuntu:v10.04"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_no_scheme_3_slash_no_tag_fail(self) -> None:
        image = "something/docker.io/library/ubuntu"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_is_neuro_registry_with_registry_prefix(self) -> None:
        assert self.parser.is_in_neuro_registry("reg.neu.ro/user/image:tag")
        assert not self.parser.is_in_neuro_registry(
            'docker.io/library/ubuntu"')

    def test_parse_as_neuro_image_with_registry_prefix(self) -> None:
        image = self.parser.parse_as_neuro_image("reg.neu.ro/user/image:tag")
        assert str(image) == "image://user/image:tag"

    def test_parse_as_neuro_image_no_scheme_3_slash_with_tag_fail(
            self) -> None:
        image = "something/docker.io/library/ubuntu:v10.04"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image)

    def test_parse_as_neuro_image_allow_tag_false_with_scheme_no_tag(
            self) -> None:
        image = "image:ubuntu"
        parsed = self.parser.parse_as_neuro_image(image, allow_tag=False)
        assert parsed == RemoteImage(name="ubuntu",
                                     tag=None,
                                     owner="alice",
                                     registry="reg.neu.ro")

    def test_parse_as_neuro_image_allow_tag_false_no_scheme_no_tag(
            self) -> None:
        image = "ubuntu"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            self.parser.parse_as_neuro_image(image, allow_tag=False)

    def test_parse_as_neuro_image_allow_tag_false_no_scheme_with_tag(
            self) -> None:
        image = "ubuntu:latest"
        with pytest.raises(ValueError, match="tag is not allowed"):
            self.parser.parse_as_neuro_image(image, allow_tag=False)

    def test_convert_to_local_image(self) -> None:
        neuro_image = RemoteImage(name="ubuntu",
                                  tag="latest",
                                  owner="artem",
                                  registry="reg.com")
        local_image = self.parser.convert_to_local_image(neuro_image)
        assert local_image == LocalImage(name="ubuntu", tag="latest")

    def test_convert_to_neuro_image(self) -> None:
        local_image = LocalImage(name="ubuntu", tag="latest")
        neuro_image = self.parser.convert_to_neuro_image(local_image)
        assert neuro_image == RemoteImage(name="ubuntu",
                                          tag="latest",
                                          owner="alice",
                                          registry="reg.neu.ro")

    def test_normalize_is_neuro_image(self) -> None:
        image = "image://~/ubuntu"
        assert self.parser.normalize(image) == "image://alice/ubuntu:latest"

    def test_normalize_is_local_image(self) -> None:
        image = "docker.io/library/ubuntu"
        assert self.parser.normalize(
            image) == "docker.io/library/ubuntu:latest"

    def test_normalize_invalid_image_name_left_as_is(self) -> None:
        image = "image://ubuntu"
        assert self.parser.normalize(image) == "image://ubuntu"

    # corner case 'image:latest'

    def test_parse_as_neuro_image__ambiguous_case__fail(self) -> None:
        url = "image:latest"
        with pytest.raises(ValueError, match="ambiguous value"):
            self.parser.parse_as_neuro_image(url)

    def test_parse_as_local_image__ambiguous_case__fail(self) -> None:
        url = "image:latest"
        with pytest.raises(ValueError, match="ambiguous value"):
            self.parser.parse_as_local_image(url)

    # other corner cases

    def test_is_in_neuro_registry__registry_has_port__neuro_image(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "image://bob/library/ubuntu:v10.04"
        assert my_parser.is_in_neuro_registry(image) is True

    def test_is_in_neuro_registry__registry_has_port__image_in_good_repo(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "localhost:5000/bob/library/ubuntu:v10.04"
        assert my_parser.is_in_neuro_registry(image) is True

    def test_is_in_neuro_registry__registry_has_port__image_in_bad_repo(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "localhost:9999/bob/library/ubuntu:v10.04"
        assert my_parser.is_in_neuro_registry(image) is False

    def test_is_in_neuro_registry__registry_has_port__local_image(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "ubuntu:v10.04"
        assert my_parser.is_in_neuro_registry(image) is False

    def test_is_in_neuro_registry__registry_has_port(self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "ubuntu:v10.04"
        parsed = my_parser.parse_as_local_image(image)
        assert parsed == LocalImage(name="ubuntu", tag="v10.04")

    def test_parse_as_neuro_image__registry_has_port__neuro_image(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "image://bob/library/ubuntu:v10.04"
        parsed = my_parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="bob",
                                     registry="localhost:5000")

    def test_parse_as_neuro_image__registry_has_port__image_in_good_repo(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "localhost:5000/bob/library/ubuntu:v10.04"
        parsed = my_parser.parse_as_neuro_image(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="bob",
                                     registry="localhost:5000")

    def test_parse_as_neuro_image__registry_has_port__image_in_bad_repo(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "localhost:9999/bob/library/ubuntu:v10.04"
        with pytest.raises(ValueError, match="scheme 'image://' is required"):
            my_parser.parse_as_neuro_image(image)

    def test_parse_remote__registry_has_port__neuro_image(self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "image://bob/library/ubuntu:v10.04"
        parsed = my_parser.parse_remote(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="bob",
                                     registry="localhost:5000")

    def test_parse_remote__registry_has_port__image_in_good_repo(self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "localhost:5000/bob/library/ubuntu:v10.04"
        parsed = my_parser.parse_remote(image)
        assert parsed == RemoteImage(name="library/ubuntu",
                                     tag="v10.04",
                                     owner="bob",
                                     registry="localhost:5000")

    def test_parse_remote__registry_has_port__image_in_other_repo(
            self) -> None:
        my_parser = _ImageNameParser(default_user="******",
                                     registry_url=URL("http://localhost:5000"))
        image = "example.com:9999/bob/library/ubuntu:v10.04"
        parsed = my_parser.parse_remote(image)
        # NOTE: "owner" is parsed only for images in neuromation registry
        assert parsed == RemoteImage(
            name="bob/library/ubuntu",
            tag="v10.04",
            owner=None,
            registry="example.com:9999",
        )