From 66d57d315a3b0a47f103adf43357fc9f867e8892 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 9 Oct 2024 12:26:04 -0400 Subject: [PATCH] Allow for filepaths to include (#161) * Allow for filepaths to include When playing with omlmd I tried to push a file which contained a ":" and oras choked on it. I belive that you should check if the file exists and only split off the last : looking for options, not the first colon. Signed-off-by: Daniel J Walsh Signed-off-by: tarilabs --- CHANGELOG.md | 1 + oras/tests/test_provider.py | 65 +++++++++++++++++++++++++++++++++---- oras/utils/fileio.py | 5 +-- oras/version.py | 2 +- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3171da..ee1d657 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and **Merged pull requests**. Critical items to know are: The versions coincide with releases on pip. Only major versions will be released as tags on Github. ## [0.0.x](https://github.com/oras-project/oras-py/tree/main) (0.0.x) + - allow for filepaths to include `:` (0.2.22) - release request (0.2.21) - add missing basic auth data for request token function in token auth backend (0.2.2) - re-enable chunked upload (0.2.1) diff --git a/oras/tests/test_provider.py b/oras/tests/test_provider.py index f91711e..2b51630 100644 --- a/oras/tests/test_provider.py +++ b/oras/tests/test_provider.py @@ -63,6 +63,65 @@ def test_annotated_registry_push(tmp_path, registry, credentials, target): ) +@pytest.mark.with_auth(False) +def test_file_contains_column(tmp_path, registry, credentials, target): + """ + Test for file containing column symbol + """ + client = oras.client.OrasClient(hostname=registry, insecure=True) + artifact = os.path.join(here, "artifact.txt") + assert os.path.exists(artifact) + + # file containing `:` + try: + contains_column = here / "some:file" + with open(contains_column, "w") as f: + f.write("hello world some:file") + + res = client.push(files=[contains_column], target=target) + assert res.status_code in [200, 201] + + files = client.pull(target, outdir=tmp_path / "download") + download = str(tmp_path / "download/some:file") + assert download in files + assert oras.utils.get_file_hash( + str(contains_column) + ) == oras.utils.get_file_hash(download) + finally: + contains_column.unlink() + + # file containing `:` as prefix, pushed with type + try: + contains_column = here / ":somefile" + with open(contains_column, "w") as f: + f.write("hello world :somefile") + + res = client.push(files=[f"{contains_column}:text/plain"], target=target) + assert res.status_code in [200, 201] + + files = client.pull(target, outdir=tmp_path / "download") + download = str(tmp_path / "download/:somefile") + assert download in files + assert oras.utils.get_file_hash( + str(contains_column) + ) == oras.utils.get_file_hash(download) + finally: + contains_column.unlink() + + # error: file does not exist + with pytest.raises(FileNotFoundError): + client.push(files=[".doesnotexist"], target=target) + + with pytest.raises(FileNotFoundError): + client.push(files=[":doesnotexist"], target=target) + + with pytest.raises(FileNotFoundError, match=r".*does:not:exists .*"): + client.push(files=["does:not:exists:text/plain"], target=target) + + with pytest.raises(FileNotFoundError, match=r".*does:not:exists .*"): + client.push(files=["does:not:exists:text/plain+ext"], target=target) + + @pytest.mark.with_auth(False) def test_chunked_push(tmp_path, registry, credentials, target): """ @@ -130,12 +189,6 @@ def test_parse_manifest(registry): assert ref == "path/to/config" assert content_type == "application/vnd.oci.image.config.v1+json" - testref = "path/to/config:application/vnd.oci.image.config.v1+json:extra" - remote = oras.provider.Registry(hostname=registry, insecure=True) - ref, content_type = remote._parse_manifest_ref(testref) - assert ref == "path/to/config" - assert content_type == "application/vnd.oci.image.config.v1+json:extra" - testref = "/dev/null:application/vnd.oci.image.manifest.v1+json" ref, content_type = remote._parse_manifest_ref(testref) assert ref == "/dev/null" diff --git a/oras/utils/fileio.py b/oras/utils/fileio.py index db30820..9aab3ae 100644 --- a/oras/utils/fileio.py +++ b/oras/utils/fileio.py @@ -346,7 +346,8 @@ def split_path_and_content(ref: str) -> PathAndOptionalContent: : return: A Tuple of the path in the reference, and the content-type if one found, otherwise None. """ - if ":" not in ref: + + if os.path.exists(ref) or ":" not in ref: return PathAndOptionalContent(ref, None) if pathlib.Path(ref).drive: @@ -370,5 +371,5 @@ def split_path_and_content(ref: str) -> PathAndOptionalContent: ) return PathAndOptionalContent(ref, None) else: - path_content_list = ref.split(":", 1) + path_content_list = ref.rsplit(":", 1) return PathAndOptionalContent(path_content_list[0], path_content_list[1]) diff --git a/oras/version.py b/oras/version.py index f9b0fc9..b278706 100644 --- a/oras/version.py +++ b/oras/version.py @@ -2,7 +2,7 @@ __copyright__ = "Copyright The ORAS Authors." __license__ = "Apache-2.0" -__version__ = "0.2.21" +__version__ = "0.2.22" AUTHOR = "Vanessa Sochat" EMAIL = "vsoch@users.noreply.github.com" NAME = "oras"