Skip to content
Draft
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
2 changes: 0 additions & 2 deletions diracx-core/src/diracx/core/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from .sources import (
ConfigSource,
ConfigSourceUrl,
LocalGitConfigSource,
RemoteGitConfigSource,
is_running_in_async_context,
)
Expand All @@ -15,7 +14,6 @@
"Config",
"ConfigSource",
"ConfigSourceUrl",
"LocalGitConfigSource",
"RemoteGitConfigSource",
"is_running_in_async_context",
)
50 changes: 12 additions & 38 deletions diracx-core/src/diracx/core/config/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ConfigSource(metaclass=ABCMeta):

# Keep a mapping between the scheme and the class
__registry: dict[str, type["ConfigSource"]] = {}
scheme: str
scheme: str | list[str]

def __init__(self, *, backend_url: ConfigSourceUrl) -> None:
# Revision cache is used to store the latest revision and its
Expand Down Expand Up @@ -102,9 +102,11 @@ def read_raw(self, hexsha: str, modified: datetime) -> Config:

def __init_subclass__(cls) -> None:
"""Keep a record of <scheme: class>."""
if cls.scheme in cls.__registry:
raise TypeError(f"{cls.scheme=} is already define")
cls.__registry[cls.scheme] = cls
schemes = cls.scheme if isinstance(cls.scheme, list) else [cls.scheme]
for scheme in schemes:
if scheme in cls.__registry:
raise TypeError(f"{scheme=} is already defined")
cls.__registry[scheme] = cls

@classmethod
def create(cls):
Expand Down Expand Up @@ -233,42 +235,14 @@ def get_git_branch_from_url(self, backend_url: ConfigSourceUrl) -> str:
return dict(backend_url.query_params()).get("branch", DEFAULT_GIT_BRANCH)


class LocalGitConfigSource(BaseGitConfigSource):
"""The configuration is stored on a local git repository
When running on multiple servers, the filesystem must be shared.
"""

scheme = "git+file"

def __init__(self, *, backend_url: ConfigSourceUrl) -> None:
super().__init__(backend_url=backend_url)
if not backend_url.path:
raise ValueError("Empty path for LocalGitConfigSource")

self.repo_location = Path(backend_url.path)
# Check if it's a valid git repository
try:
sh.git(
"rev-parse",
"--git-dir",
_cwd=self.repo_location,
_tty_out=False,
_async=False,
)
except sh.ErrorReturnCode as e:
raise ValueError(
f"{self.repo_location} is not a valid git repository"
) from e
sh.git.checkout(self.git_branch, _cwd=self.repo_location, _async=False)

def __hash__(self):
return hash(self.repo_location)


class RemoteGitConfigSource(BaseGitConfigSource):
"""Use a remote directory as a config source."""
"""Use a remote or local git repository as a config source.

Supports both remote URLs (git+https://) and local file URLs (git+file://).
The repository is cloned to a temporary directory.
"""

scheme = "git+https"
scheme = ["git+https", "git+file"]

def __init__(self, *, backend_url: ConfigSourceUrl) -> None:
super().__init__(backend_url=backend_url)
Expand Down
30 changes: 30 additions & 0 deletions diracx-core/tests/test_config_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,33 @@ def test_remote_git_config_source(monkeypatch, repo_url):
assert isinstance(modified, datetime.datetime)
result = remote_conf.read_raw(hexsha, modified)
assert isinstance(result, Config)


def test_file_url_config_source(tmp_path):
"""Test that file URLs work with RemoteGitConfigSource."""
from git import Repo

# Create a test git repository
repo = Repo.init(tmp_path, initial_branch="master")
cs_file = tmp_path / "default.yml"
example_cs = Config.model_validate(
{
"DIRAC": {},
"Registry": {},
"Operations": {},
}
)
cs_file.write_text(example_cs.model_dump_json())
repo.index.add([cs_file])
repo.index.commit("Initial commit")

# Test with git+file:// URL
file_url = f"git+file://{tmp_path}"
config_source = ConfigSource.create_from_url(backend_url=file_url)
assert isinstance(config_source, RemoteGitConfigSource)

hexsha, modified = config_source.latest_revision()
assert isinstance(hexsha, str)
assert isinstance(modified, datetime.datetime)
result = config_source.read_raw(hexsha, modified)
assert isinstance(result, Config)
Loading