Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added bindings for link reroute functionality. #1

Merged
merged 3 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/pygobbler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@
from .reject_probation import reject_probation
from .set_permissions import set_permissions
from .unpack_path import unpack_path
from .reroute_links import reroute_links
2 changes: 2 additions & 0 deletions src/pygobbler/remove_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
def remove_asset(project: str, asset: str, staging: str, url: str, force: bool = False):
"""
Remove an asset of a project from the registry.
This should only be performed by Gobbler instance administrators.
Consider running :py:func:`~pygobbler.reroute_links.reroute_links` beforehand to avoid dangling references to files in this asset.
Args:
project:
Expand Down
2 changes: 2 additions & 0 deletions src/pygobbler/remove_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
def remove_project(project: str, staging: str, url: str):
"""
Remove a project from the registry.
This should only be performed by Gobbler instance administrators.
Consider running :py:func:`~pygobbler.reroute_links.reroute_links` beforehand to avoid dangling references to files in this project.
Args:
project:
Expand Down
2 changes: 2 additions & 0 deletions src/pygobbler/remove_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
def remove_version(project: str, asset: str, version: str, staging: str, url: str, force: bool = False):
"""
Remove a version of a project asset from the registry.
This should only be performed by Gobbler instance administrators.
Consider running :py:func:`~pygobbler.reroute_links.reroute_links` beforehand to avoid dangling references to files in this version.
Args:
project:
Expand Down
47 changes: 47 additions & 0 deletions src/pygobbler/reroute_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import List
from ._utils import dump_request


def reroute_links(to_delete: List, staging: str, url: str, dry_run: bool = False) -> List:
"""Reroute symbolic links to files in directories that are to be deleted, e.g., by :py:func:`~pygobbler.remove_project.remove_project`.
This preserves the validity of links within the Gobbler registry.

Note that rerouting does not actually delete the directories specified in ``to_delete``.
Deletion requires separate invocations of :py:func:`~pygobbler.remove_project.remove_project` and friends - preferably after the user has verified that rerouting was successful!

Rerouting is not necessary if ``to_delete`` consists only of probational versions, or projects/assets containing only probational versions.
The Gobbler should never create links to files in probational version directories.

Args:
to_delete:
List of projects, assets or versions to be deleted.
Each entry should be a dicionary containing at least the ``project`` name.
When deleting an asset, the inner list should contain an additional ``asset`` name.
When deleting a version, the inner list should contain additional ``asset`` and ``version`` names.
Different inner lists may specify different projects, assets or versions.

staging:
Path to the staging directory.

url:
URL for the Gobbler REST API.

dry_run:
Whether to perform a dry run of the rerouting.

Returns:
List of dictionaries.
Each dictionary represents a rerouting action and contains the following fields.

- ``path``, string containing the path to a symbolic link in the registry that was changed by rerouting.
- ``copy``, boolean indicating whether the link at ``path`` was replaced by a copy of its target file.
If ``False``, the link was merely updated to refer to a new target file.
- ``source``, the path to the target file that caused rerouting of ``path``.
Specifically, this is a file in one of the to-be-deleted directories specified in ``to_delete``.
If ``copy = TRUE``, this is the original linked-to file that was copied to ``path``.

If ``dry_run = False``, the registry is modified as described by the rerouting actions.
Otherwise, no modifications are performed to the registry.
"""
out = dump_request(staging, url, "reroute_links", { "to_delete": to_delete, "dry_run": dry_run })
return out["changes"]
2 changes: 1 addition & 1 deletion src/pygobbler/start_gobbler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def start_gobbler(
registry: Optional[str] = None,
port: Optional[int] = None,
wait: float = 1,
version: str = "0.3.9",
version: str = "0.3.10",
overwrite: bool = False) -> Tuple[bool, str, str, str]:
"""
Start a test Gobbler service.
Expand Down
32 changes: 32 additions & 0 deletions tests/test_reroute_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pygobbler as pyg
import os


def test_reroute_links():
_, staging, registry, url = pyg.start_gobbler()

pyg.remove_project("test-reroute", staging=staging, url=url)
pyg.create_project("test-reroute", staging=staging, url=url)

src = pyg.allocate_upload_directory(staging)
with open(os.path.join(src, "foo"), "w") as handle:
handle.write("BAR")

pyg.upload_directory("test-reroute", "simple", "v1", src, staging=staging, url=url)
pyg.upload_directory("test-reroute", "simple", "v2", src, staging=staging, url=url)
pyg.upload_directory("test-reroute", "simple", "v3", src, staging=staging, url=url)

actions = pyg.reroute_links([{"project":"test-reroute", "asset":"simple", "version":"v1"}], staging=staging, url=url, dry_run=True)
print(actions)
assert all([x["source"] == "test-reroute/simple/v1/foo" for x in actions])
all_paths = [x["path"] for x in actions]
assert "test-reroute/simple/v2/foo" in all_paths
assert "test-reroute/simple/v3/foo" in all_paths
all_copy = [x["copy"] for x in actions]
assert all_copy[all_paths.index("test-reroute/simple/v2/foo")]
assert not all_copy[all_paths.index("test-reroute/simple/v3/foo")]
assert os.path.islink(os.path.join(registry, "test-reroute/simple/v2/foo"))

actions2 = pyg.reroute_links([{"project":"test-reroute", "asset":"simple", "version":"v1"}], staging=staging, url=url)
assert actions == actions2
assert not os.path.islink(os.path.join(registry, "test-reroute/simple/v2/foo"))