gwdatafind-server 2.1.0__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/.gitlab-ci.yml +1 -0
- gwdatafind_server-2.3.0/AGENTS.md +94 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/CHANGELOG.md +18 -0
- gwdatafind_server-2.3.0/CLAUDE.md +94 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/Dockerfile +9 -9
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/PKG-INFO +1 -1
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/gunicorn.conf.py +7 -9
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/gwdatafind-server.toml +1 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docker-compose.yml +12 -5
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/changelog.md +18 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/configuration.md +1 -3
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/auth/docker-compose.yml +9 -2
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/auth/gwdatafind-server.toml +4 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/diskcache/docker-compose.yml +10 -1
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/examples/diskcache/gwdatafind-server.toml +1 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/__init__.py +14 -8
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/_version.py +3 -3
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/app.py +28 -22
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/diskcache.py +1 -2
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/pelican.py +192 -69
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/PKG-INFO +1 -1
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/SOURCES.txt +4 -0
- gwdatafind_server-2.3.0/gwdatafind_server.egg-info/scm_file_list.json +82 -0
- gwdatafind_server-2.3.0/gwdatafind_server.egg-info/scm_version.json +8 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/pyproject.toml +13 -4
- gwdatafind_server-2.3.0/tests/test_app.py +122 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_source_pelican.py +145 -13
- gwdatafind_server-2.1.0/tests/test_app.py +0 -6
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/.gitignore +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/.markdownlint.yml +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/LICENSE +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/MANIFEST.in +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/README.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/docker-compose.yml +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/config/gwdatafind-server.service +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/diskcache/Makefile +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/diskcache/create_testdata.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/diskcache/diskcache.rsc +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/api/index.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/api/ldr.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/api/v1.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/auth.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/diskcache.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/gwosc.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/examples/index.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/deploy/index.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/index.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/overview.md +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/docs/requirements.txt +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/__init__.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/base.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/ldr.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/utils.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/api/v1.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/auth/__init__.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/auth/core.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/auth/scitoken.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/cache.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/config.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/credential/__init__.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/credential/credential.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/credential/scitoken.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/gwdatafind_server.wsgi +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/__init__.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/source/source.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server/urls.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/dependency_links.txt +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/requires.txt +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/gwdatafind_server.egg-info/top_level.txt +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/mkdocs.yml +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/setup.cfg +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/__init__.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/cache-0x00ff.dat +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/cache-0x0101.dat +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/conftest.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/grid-mapfile +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/gwdatafind-server.ini +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/gwdatafind-server.toml +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_api_v1.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_auth_override.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_auth_scitoken.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_cache.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_config.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_credential.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_credential_scitoken.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_source.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_source_diskcache.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/test_urls.py +0 -0
- {gwdatafind_server-2.1.0 → gwdatafind_server-2.3.0}/tests/utils.py +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents (and humans) working in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
GWDataFind Server is a [Flask](https://flask.palletsprojects.com/) application that
|
|
8
|
+
implements the HTTP API used to discover URLs for gravitational-wave observatory data
|
|
9
|
+
files.
|
|
10
|
+
It is developed by the LIGO Scientific Collaboration / IGWN and used in production
|
|
11
|
+
to serve data discovery requests for LIGO/Virgo/KAGRA data.
|
|
12
|
+
|
|
13
|
+
The accompanying Python client library (`gwdatafind`, not in this repo) is what most
|
|
14
|
+
end users interact with; this repo is the server side of that API.
|
|
15
|
+
|
|
16
|
+
Canonical docs live in `docs/` (built with MkDocs, published at
|
|
17
|
+
<https://computing.docs.ligo.org/gwdatafind/server/>).
|
|
18
|
+
Read `docs/overview.md` first for architecture,
|
|
19
|
+
and `docs/configuration.md` for the full config schema —
|
|
20
|
+
both are kept up to date and should be treated as source of truth over any summary here.
|
|
21
|
+
|
|
22
|
+
## Architecture (brief)
|
|
23
|
+
|
|
24
|
+
- The app (`gwdatafind_server/app.py`, `GWDataFindApp`) runs one or more **sources**
|
|
25
|
+
(`gwdatafind_server/source/`: `diskcache`, `pelican`) on independent async schedules
|
|
26
|
+
(via `flask-apscheduler`). Each source discovers fully-qualified URLs
|
|
27
|
+
(`file://`, `pelican://`, etc.) plus metadata (observatory, dataset, format, GPS
|
|
28
|
+
segment) and feeds it into an in-memory `Cache` (`gwdatafind_server/cache.py`).
|
|
29
|
+
- **URL transforms** (`gwdatafind_server/urls.py`, configured under `[url.transform]`)
|
|
30
|
+
rewrite URLs between schemes (e.g. `pelican://` → `https://`) at query time.
|
|
31
|
+
- **Auth** (`gwdatafind_server/auth/`) supports SciToken bearer-token authorization and
|
|
32
|
+
per-IP/subnet overrides.
|
|
33
|
+
- **Credentials** (`gwdatafind_server/credential/`) are refreshed on a schedule and
|
|
34
|
+
attached to sources that need them (e.g. SciTokens obtained via Kerberos keytab for
|
|
35
|
+
Pelican access).
|
|
36
|
+
- **API** (`gwdatafind_server/api/`): versioned HTTP endpoints (`ldr` legacy, `v1`
|
|
37
|
+
current) built on the shared cache; see `docs/api/`.
|
|
38
|
+
- Configuration is TOML (`gwdatafind_server/config.py`), one config file drives the
|
|
39
|
+
whole app. `python3 -m gwdatafind_server.config <file>` parses and dumps it as JSON
|
|
40
|
+
for validation.
|
|
41
|
+
|
|
42
|
+
## Repo layout
|
|
43
|
+
|
|
44
|
+
- `gwdatafind_server/` — the application package (source of truth for behavior).
|
|
45
|
+
- `tests/` — pytest suite, one `test_*.py` per module roughly mirroring the package.
|
|
46
|
+
- `docs/` — MkDocs site (nav defined in `mkdocs.yml`); includes API reference and
|
|
47
|
+
deployment examples (`docs/deploy/examples/`).
|
|
48
|
+
- `examples/` — runnable docker-compose deployment examples (diskcache, auth/SciToken).
|
|
49
|
+
- `config/` — example/reference config files (TOML, gunicorn, systemd unit) referenced
|
|
50
|
+
by the docs via `include-markdown`; treat as documentation fixtures, not scratch space.
|
|
51
|
+
- `Dockerfile`, `docker-compose.yml` — container build/quick-start.
|
|
52
|
+
- `.gitlab-ci.yml` — CI pipeline (GitLab, not GitHub). Runs lint/type-check/tests across
|
|
53
|
+
Python 3.11–3.14, builds the Docker image, and builds the MkDocs site.
|
|
54
|
+
|
|
55
|
+
## Development practices
|
|
56
|
+
|
|
57
|
+
- **Dependency management**: [`uv`](https://docs.astral.sh/uv/).
|
|
58
|
+
Use `uv run …` / `uv sync --group dev` rather than raw `pip`.
|
|
59
|
+
- **Tests**: `pytest` (`uv run pytest`).
|
|
60
|
+
Config in `pyproject.toml` (`[tool.pytest.ini_options]`);
|
|
61
|
+
warnings are errors (`filterwarnings = ["error", ...]`),
|
|
62
|
+
so new code must not raise unexpected warnings.
|
|
63
|
+
Coverage is measured against `gwdatafind_server`.
|
|
64
|
+
- **Linting**: `ruff`, with `select = ["ALL"]` and an explicit ignore list in
|
|
65
|
+
`pyproject.toml`.
|
|
66
|
+
Run `uv run ruff check` before considering work done.
|
|
67
|
+
Per-directory ignores exist for `tests/`, `config/`, `docs/`.
|
|
68
|
+
- **Type checking**: [`ty`](https://github.com/astral-sh/ty) (not mypy) —
|
|
69
|
+
see `[tool.ty.src]` in `pyproject.toml`.
|
|
70
|
+
Note the codebase mostly ignores annotation lint rules (`ANN` is ruff-ignored)
|
|
71
|
+
but does use `from __future__ import annotations` and `TYPE_CHECKING` blocks.
|
|
72
|
+
- **Docs**: MkDocs + `mkdocs-material`-based theme, built via a shared CI component.
|
|
73
|
+
Markdown is linted with `markdownlint` (`.markdownlint.yml`); config docs pull live
|
|
74
|
+
examples from `config/` via the `include-markdown` plugin, so keep those files valid.
|
|
75
|
+
- **Commit style**: Conventional-commit-like scoped messages are the norm, e.g.
|
|
76
|
+
`feat(config): add support for source update jitter`,
|
|
77
|
+
`fix(pelican): don't skip recursion using cache`,
|
|
78
|
+
`build(docker-compose): add volume for pelican caches`.
|
|
79
|
+
Follow this pattern.
|
|
80
|
+
- **Workflow**: Development happens on GitLab (`git.ligo.org/computing/gwdatafind/server`)
|
|
81
|
+
via merge requests into `main`;
|
|
82
|
+
releases are tagged and generate `CHANGELOG.md` entries per version.
|
|
83
|
+
Licensed GPL-3.0-or-later — keep the copyright/license header convention
|
|
84
|
+
(`# Copyright (c) 2019-2025 Cardiff University` /
|
|
85
|
+
`# Licensed under GPLv3+ - see LICENSE`) on new source files.
|
|
86
|
+
|
|
87
|
+
## Sensitive areas
|
|
88
|
+
|
|
89
|
+
- **Auth/credential code** (`gwdatafind_server/auth/`, `gwdatafind_server/credential/`)
|
|
90
|
+
gates access to (sometimes proprietary/embargoed) LIGO/Virgo/KAGRA data —
|
|
91
|
+
change carefully and preserve existing test coverage.
|
|
92
|
+
- **URL transform logic** (`gwdatafind_server/urls.py`) is order-sensitive and affects
|
|
93
|
+
which URLs get served for which scheme; see the "Transform behavior" warning in
|
|
94
|
+
`docs/configuration.md` before changing matching/precedence semantics.
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0 - 2025-07-06 { #2.3.0 }
|
|
4
|
+
|
|
5
|
+
- Load source cache upfront during app startup, use concurrent gunicorn workers
|
|
6
|
+
([!133](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/133))
|
|
7
|
+
- Improve Pelican sqlite db transaction locking
|
|
8
|
+
([!134](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/134))
|
|
9
|
+
|
|
10
|
+
[Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.3.0)
|
|
11
|
+
|
|
12
|
+
## 2.2.0 - 2025-04-09 { #2.2.0 }
|
|
13
|
+
|
|
14
|
+
- Fix directory recursion with Pelican memory cache
|
|
15
|
+
([!129](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/129))
|
|
16
|
+
- Add Pelican cache versioning and compatibility checks
|
|
17
|
+
([!130](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/130))
|
|
18
|
+
|
|
19
|
+
[Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.2.0)
|
|
20
|
+
|
|
3
21
|
## 2.1.0 - 2025-03-26 { #2.1.0 }
|
|
4
22
|
|
|
5
23
|
- Add Pelican database cache file
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents (and humans) working in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
GWDataFind Server is a [Flask](https://flask.palletsprojects.com/) application that
|
|
8
|
+
implements the HTTP API used to discover URLs for gravitational-wave observatory data
|
|
9
|
+
files.
|
|
10
|
+
It is developed by the LIGO Scientific Collaboration / IGWN and used in production
|
|
11
|
+
to serve data discovery requests for LIGO/Virgo/KAGRA data.
|
|
12
|
+
|
|
13
|
+
The accompanying Python client library (`gwdatafind`, not in this repo) is what most
|
|
14
|
+
end users interact with; this repo is the server side of that API.
|
|
15
|
+
|
|
16
|
+
Canonical docs live in `docs/` (built with MkDocs, published at
|
|
17
|
+
<https://computing.docs.ligo.org/gwdatafind/server/>).
|
|
18
|
+
Read `docs/overview.md` first for architecture,
|
|
19
|
+
and `docs/configuration.md` for the full config schema —
|
|
20
|
+
both are kept up to date and should be treated as source of truth over any summary here.
|
|
21
|
+
|
|
22
|
+
## Architecture (brief)
|
|
23
|
+
|
|
24
|
+
- The app (`gwdatafind_server/app.py`, `GWDataFindApp`) runs one or more **sources**
|
|
25
|
+
(`gwdatafind_server/source/`: `diskcache`, `pelican`) on independent async schedules
|
|
26
|
+
(via `flask-apscheduler`). Each source discovers fully-qualified URLs
|
|
27
|
+
(`file://`, `pelican://`, etc.) plus metadata (observatory, dataset, format, GPS
|
|
28
|
+
segment) and feeds it into an in-memory `Cache` (`gwdatafind_server/cache.py`).
|
|
29
|
+
- **URL transforms** (`gwdatafind_server/urls.py`, configured under `[url.transform]`)
|
|
30
|
+
rewrite URLs between schemes (e.g. `pelican://` → `https://`) at query time.
|
|
31
|
+
- **Auth** (`gwdatafind_server/auth/`) supports SciToken bearer-token authorization and
|
|
32
|
+
per-IP/subnet overrides.
|
|
33
|
+
- **Credentials** (`gwdatafind_server/credential/`) are refreshed on a schedule and
|
|
34
|
+
attached to sources that need them (e.g. SciTokens obtained via Kerberos keytab for
|
|
35
|
+
Pelican access).
|
|
36
|
+
- **API** (`gwdatafind_server/api/`): versioned HTTP endpoints (`ldr` legacy, `v1`
|
|
37
|
+
current) built on the shared cache; see `docs/api/`.
|
|
38
|
+
- Configuration is TOML (`gwdatafind_server/config.py`), one config file drives the
|
|
39
|
+
whole app. `python3 -m gwdatafind_server.config <file>` parses and dumps it as JSON
|
|
40
|
+
for validation.
|
|
41
|
+
|
|
42
|
+
## Repo layout
|
|
43
|
+
|
|
44
|
+
- `gwdatafind_server/` — the application package (source of truth for behavior).
|
|
45
|
+
- `tests/` — pytest suite, one `test_*.py` per module roughly mirroring the package.
|
|
46
|
+
- `docs/` — MkDocs site (nav defined in `mkdocs.yml`); includes API reference and
|
|
47
|
+
deployment examples (`docs/deploy/examples/`).
|
|
48
|
+
- `examples/` — runnable docker-compose deployment examples (diskcache, auth/SciToken).
|
|
49
|
+
- `config/` — example/reference config files (TOML, gunicorn, systemd unit) referenced
|
|
50
|
+
by the docs via `include-markdown`; treat as documentation fixtures, not scratch space.
|
|
51
|
+
- `Dockerfile`, `docker-compose.yml` — container build/quick-start.
|
|
52
|
+
- `.gitlab-ci.yml` — CI pipeline (GitLab, not GitHub). Runs lint/type-check/tests across
|
|
53
|
+
Python 3.11–3.14, builds the Docker image, and builds the MkDocs site.
|
|
54
|
+
|
|
55
|
+
## Development practices
|
|
56
|
+
|
|
57
|
+
- **Dependency management**: [`uv`](https://docs.astral.sh/uv/).
|
|
58
|
+
Use `uv run …` / `uv sync --group dev` rather than raw `pip`.
|
|
59
|
+
- **Tests**: `pytest` (`uv run pytest`).
|
|
60
|
+
Config in `pyproject.toml` (`[tool.pytest.ini_options]`);
|
|
61
|
+
warnings are errors (`filterwarnings = ["error", ...]`),
|
|
62
|
+
so new code must not raise unexpected warnings.
|
|
63
|
+
Coverage is measured against `gwdatafind_server`.
|
|
64
|
+
- **Linting**: `ruff`, with `select = ["ALL"]` and an explicit ignore list in
|
|
65
|
+
`pyproject.toml`.
|
|
66
|
+
Run `uv run ruff check` before considering work done.
|
|
67
|
+
Per-directory ignores exist for `tests/`, `config/`, `docs/`.
|
|
68
|
+
- **Type checking**: [`ty`](https://github.com/astral-sh/ty) (not mypy) —
|
|
69
|
+
see `[tool.ty.src]` in `pyproject.toml`.
|
|
70
|
+
Note the codebase mostly ignores annotation lint rules (`ANN` is ruff-ignored)
|
|
71
|
+
but does use `from __future__ import annotations` and `TYPE_CHECKING` blocks.
|
|
72
|
+
- **Docs**: MkDocs + `mkdocs-material`-based theme, built via a shared CI component.
|
|
73
|
+
Markdown is linted with `markdownlint` (`.markdownlint.yml`); config docs pull live
|
|
74
|
+
examples from `config/` via the `include-markdown` plugin, so keep those files valid.
|
|
75
|
+
- **Commit style**: Conventional-commit-like scoped messages are the norm, e.g.
|
|
76
|
+
`feat(config): add support for source update jitter`,
|
|
77
|
+
`fix(pelican): don't skip recursion using cache`,
|
|
78
|
+
`build(docker-compose): add volume for pelican caches`.
|
|
79
|
+
Follow this pattern.
|
|
80
|
+
- **Workflow**: Development happens on GitLab (`git.ligo.org/computing/gwdatafind/server`)
|
|
81
|
+
via merge requests into `main`;
|
|
82
|
+
releases are tagged and generate `CHANGELOG.md` entries per version.
|
|
83
|
+
Licensed GPL-3.0-or-later — keep the copyright/license header convention
|
|
84
|
+
(`# Copyright (c) 2019-2025 Cardiff University` /
|
|
85
|
+
`# Licensed under GPLv3+ - see LICENSE`) on new source files.
|
|
86
|
+
|
|
87
|
+
## Sensitive areas
|
|
88
|
+
|
|
89
|
+
- **Auth/credential code** (`gwdatafind_server/auth/`, `gwdatafind_server/credential/`)
|
|
90
|
+
gates access to (sometimes proprietary/embargoed) LIGO/Virgo/KAGRA data —
|
|
91
|
+
change carefully and preserve existing test coverage.
|
|
92
|
+
- **URL transform logic** (`gwdatafind_server/urls.py`) is order-sensitive and affects
|
|
93
|
+
which URLs get served for which scheme; see the "Transform behavior" warning in
|
|
94
|
+
`docs/configuration.md` before changing matching/precedence semantics.
|
|
@@ -2,18 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
FROM ghcr.io/astral-sh/uv:python3.14-bookworm AS build
|
|
4
4
|
|
|
5
|
-
ARG SETUPTOOLS_SCM_PRETEND_VERSION
|
|
6
|
-
|
|
7
5
|
WORKDIR /code
|
|
8
6
|
|
|
9
|
-
COPY .git .git
|
|
10
7
|
COPY . ./
|
|
11
8
|
|
|
12
9
|
# Generate a wheel
|
|
13
|
-
RUN
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
&& uv build --wheel
|
|
10
|
+
RUN git config --global safe.directory $(pwd) \
|
|
11
|
+
&& rm -rf docker_dist/*.whl \
|
|
12
|
+
&& uv build --wheel --out-dir docker_dist/
|
|
17
13
|
|
|
18
14
|
# -- install
|
|
19
15
|
|
|
@@ -28,10 +24,14 @@ RUN uv venv ${VIRTUAL_ENV}
|
|
|
28
24
|
|
|
29
25
|
# Install the requirements
|
|
30
26
|
COPY pyproject.toml .
|
|
31
|
-
RUN uv pip install
|
|
27
|
+
RUN uv pip install \
|
|
28
|
+
-r pyproject.toml \
|
|
29
|
+
--extra credentials \
|
|
30
|
+
--extra gunicorn \
|
|
31
|
+
--extra pelican
|
|
32
32
|
|
|
33
33
|
# Install the app
|
|
34
|
-
COPY --from=build /code/
|
|
34
|
+
COPY --from=build /code/docker_dist/*.whl /code
|
|
35
35
|
RUN WHEEL=$(ls /code/*.whl) \
|
|
36
36
|
&& uv pip install $WHEEL[credentials,gunicorn,pelican]
|
|
37
37
|
|
|
@@ -11,20 +11,17 @@ bind = "0.0.0.0:8080"
|
|
|
11
11
|
backlog = 2048
|
|
12
12
|
|
|
13
13
|
# Worker processes
|
|
14
|
-
workers =
|
|
14
|
+
workers = 2
|
|
15
15
|
worker_class = "gthread"
|
|
16
16
|
worker_connections = 1000
|
|
17
|
-
threads =
|
|
17
|
+
threads = 4
|
|
18
18
|
timeout = 30
|
|
19
19
|
keepalive = 2
|
|
20
20
|
|
|
21
|
-
# Restart workers after this many requests, with up to this much jitter
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# Worker recycling
|
|
26
|
-
max_worker_memory = 134217728 # 128MB in bytes
|
|
27
|
-
worker_tmp_dir = "/dev/shm"
|
|
21
|
+
# Restart workers after this many requests, with up to this much jitter.
|
|
22
|
+
# Keep this high enough to avoid frequent cache rebuilds.
|
|
23
|
+
max_requests = 10000
|
|
24
|
+
max_requests_jitter = 1000
|
|
28
25
|
|
|
29
26
|
# Security
|
|
30
27
|
limit_request_line = 4094
|
|
@@ -104,6 +101,7 @@ if os.environ.get("GWDATAFIND_SERVER_DEBUG"):
|
|
|
104
101
|
loglevel = "debug"
|
|
105
102
|
reload = True
|
|
106
103
|
workers = 1
|
|
104
|
+
threads = 4
|
|
107
105
|
|
|
108
106
|
if gwdfworkers := os.environ.get("GWDATAFIND_WORKERS"):
|
|
109
107
|
workers = int(gwdfworkers)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
services:
|
|
11
11
|
gwdatafind:
|
|
12
12
|
container_name: gwdatafind-server
|
|
13
|
-
image: gwdatafind
|
|
13
|
+
image: ${GWDATAFIND_SERVER_IMAGE-containers.ligo.org/computing/gwdatafind/server:latest}
|
|
14
14
|
ports:
|
|
15
15
|
- "127.0.0.1:8080:8080" # Bind only to localhost
|
|
16
16
|
restart: unless-stopped
|
|
@@ -26,13 +26,16 @@ services:
|
|
|
26
26
|
# Volumes
|
|
27
27
|
volumes:
|
|
28
28
|
- type: bind
|
|
29
|
-
source: config/gwdatafind-server.toml
|
|
29
|
+
source: ${GWDATAFIND_SERVER_CONFIG-config/gwdatafind-server.toml}
|
|
30
30
|
target: /etc/gwdatafind/gwdatafind-server.toml
|
|
31
31
|
read_only: true
|
|
32
32
|
- type: tmpfs
|
|
33
33
|
target: /tmp
|
|
34
34
|
tmpfs:
|
|
35
35
|
size: 100m
|
|
36
|
+
- type: volume
|
|
37
|
+
source: gwdatafind-data
|
|
38
|
+
target: /var/lib/gwdatafind
|
|
36
39
|
|
|
37
40
|
# Environment
|
|
38
41
|
environment:
|
|
@@ -41,11 +44,11 @@ services:
|
|
|
41
44
|
|
|
42
45
|
# Health check
|
|
43
46
|
healthcheck:
|
|
44
|
-
test: ["CMD", "
|
|
47
|
+
test: ["CMD-SHELL", "pgrep -f 'gunicorn.*gwdatafind_server:create_app\\(\\)' >/dev/null"]
|
|
45
48
|
interval: 30s
|
|
46
|
-
timeout:
|
|
49
|
+
timeout: 5s
|
|
47
50
|
retries: 3
|
|
48
|
-
start_period:
|
|
51
|
+
start_period: 30s
|
|
49
52
|
|
|
50
53
|
# Logging
|
|
51
54
|
logging:
|
|
@@ -58,3 +61,7 @@ services:
|
|
|
58
61
|
labels:
|
|
59
62
|
- "com.example.service=gwdatafind-server"
|
|
60
63
|
- "com.example.version=latest"
|
|
64
|
+
|
|
65
|
+
volumes:
|
|
66
|
+
gwdatafind-data:
|
|
67
|
+
driver: local
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0 - 2025-07-06 { #2.3.0 }
|
|
4
|
+
|
|
5
|
+
- Load source cache upfront during app startup, use concurrent gunicorn workers
|
|
6
|
+
([!133](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/133))
|
|
7
|
+
- Improve Pelican sqlite db transaction locking
|
|
8
|
+
([!134](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/134))
|
|
9
|
+
|
|
10
|
+
[Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.3.0)
|
|
11
|
+
|
|
12
|
+
## 2.2.0 - 2025-04-09 { #2.2.0 }
|
|
13
|
+
|
|
14
|
+
- Fix directory recursion with Pelican memory cache
|
|
15
|
+
([!129](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/129))
|
|
16
|
+
- Add Pelican cache versioning and compatibility checks
|
|
17
|
+
([!130](https://git.ligo.org/computing/gwdatafind/server/-/merge_requests/130))
|
|
18
|
+
|
|
19
|
+
[Full details](https://git.ligo.org/computing/gwdatafind/server/-/releases/2.2.0)
|
|
20
|
+
|
|
3
21
|
## 2.1.0 - 2025-03-26 { #2.1.0 }
|
|
4
22
|
|
|
5
23
|
- Add Pelican database cache file
|
|
@@ -257,6 +257,7 @@ All source types accept the following options:
|
|
|
257
257
|
| `name` | [string](https://toml.io/en/v1.0.0#string) | [optional] A unique name for this source (defaults to "{type}_source_{index}") |
|
|
258
258
|
| `interval` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] How often (in seconds) to query for new data |
|
|
259
259
|
| `initial_delay` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] Delay (in seconds) before the first data collection |
|
|
260
|
+
| `jitter` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] Maximum random jitter (in seconds) to add to the interval between data collections (default: 0) |
|
|
260
261
|
| `credential` | [string](https://toml.io/en/v1.0.0#string) | [optional] Name of the credential to use for authentication |
|
|
261
262
|
| `split_sites` | [boolean](https://toml.io/en/v1.0.0#boolean) | [optional] If `true`, multi-site files will be indexed for individual sites, as well as the combination (default: `false`) |
|
|
262
263
|
|
|
@@ -270,7 +271,6 @@ other keys:
|
|
|
270
271
|
| `host` | [string](https://toml.io/en/v1.0.0#string) | The address of the diskcache instance |
|
|
271
272
|
| `port` | [integer](https://toml.io/en/v1.0.0#integer) | The port to talk to on the diskcache host |
|
|
272
273
|
| `path` | [string](https://toml.io/en/v1.0.0#string) | Alternatively, the path of a Diskcache ASCII output file to read |
|
|
273
|
-
| `interval` | [integer](https://toml.io/en/v1.0.0#integer) | How often (in seconds) to request information from the diskcache host, or to reread the diskcache file |
|
|
274
274
|
|
|
275
275
|
!!! example "Diskcache input configuration"
|
|
276
276
|
|
|
@@ -306,7 +306,6 @@ other keys:
|
|
|
306
306
|
| `token` | [boolean](https://toml.io/en/v1.0.0#boolean) | If `true`, a token will be included in requests to the Pelican host (default `false`) |
|
|
307
307
|
| `credential` | [string](https://toml.io/en/v1.0.0#string) | [optional] Name of the credential to use for authentication (alternative to `token`) |
|
|
308
308
|
| `cachefile` | [string](https://toml.io/en/v1.0.0#string) | [optional] Path to SQLite cache file for persistent storage of directory listings (improves startup performance) |
|
|
309
|
-
| `interval` | [integer](https://toml.io/en/v1.0.0#integer) | How often (in seconds) to query for new data from the Pelican host |
|
|
310
309
|
|
|
311
310
|
!!! note "Pelican directory listings are cached"
|
|
312
311
|
|
|
@@ -353,7 +352,6 @@ other keys:
|
|
|
353
352
|
| `principal` | [string](https://toml.io/en/v1.0.0#string) | [optional] The Kerberos principal to authenticate as |
|
|
354
353
|
| `audience` | [string](https://toml.io/en/v1.0.0#string) | The intended audience for the SciToken |
|
|
355
354
|
| `scopes` | [array](https://toml.io/en/v1.0.0#array) of [string](https://toml.io/en/v1.0.0#string) | [optional] List of scopes to request for the SciToken |
|
|
356
|
-
| `interval` | [integer](https://toml.io/en/v1.0.0#integer) | [optional] How often (in seconds) to refresh the credential (default: 3600 = 1 hour) |
|
|
357
355
|
|
|
358
356
|
!!! example "SciToken credential configuration"
|
|
359
357
|
|
|
@@ -10,7 +10,7 @@ services:
|
|
|
10
10
|
# Rebuild image with Kerberos keytab embedded securely
|
|
11
11
|
context: .
|
|
12
12
|
dockerfile_inline: |
|
|
13
|
-
FROM containers.ligo.org/computing/gwdatafind/server:latest
|
|
13
|
+
FROM ${GWDATAFIND_SERVER_IMAGE-containers.ligo.org/computing/gwdatafind/server:latest}
|
|
14
14
|
COPY --chown=gwdatafind:gwdatafind --chmod=400 pelican.keytab /pelican.keytab
|
|
15
15
|
restart: unless-stopped
|
|
16
16
|
ports:
|
|
@@ -26,9 +26,12 @@ services:
|
|
|
26
26
|
# Volumes
|
|
27
27
|
volumes:
|
|
28
28
|
- type: bind
|
|
29
|
-
source:
|
|
29
|
+
source: ${GWDATAFIND_SERVER_CONFIG-./gwdatafind-server.toml}
|
|
30
30
|
target: /etc/gwdatafind/gwdatafind-server.toml
|
|
31
31
|
read_only: true
|
|
32
|
+
- type: volume
|
|
33
|
+
source: gwdatafind-data
|
|
34
|
+
target: /var/lib/gwdatafind
|
|
32
35
|
read_only: true # Read-only root filesystem
|
|
33
36
|
tmpfs:
|
|
34
37
|
- /tmp:noexec,nosuid,size=100m
|
|
@@ -64,3 +67,7 @@ services:
|
|
|
64
67
|
options:
|
|
65
68
|
max-size: "10m"
|
|
66
69
|
max-file: "3"
|
|
70
|
+
|
|
71
|
+
volumes:
|
|
72
|
+
gwdatafind-data:
|
|
73
|
+
driver: local
|
|
@@ -38,6 +38,7 @@ type = "pelican"
|
|
|
38
38
|
federation_url = "osg-htc.org"
|
|
39
39
|
path = "/gwdata"
|
|
40
40
|
interval = 10800
|
|
41
|
+
jitter = 600
|
|
41
42
|
cachefile = "/var/lib/gwdatafind/pelican-gwosc.db"
|
|
42
43
|
|
|
43
44
|
# Private LIGO data in Pelican
|
|
@@ -47,6 +48,7 @@ type = "pelican"
|
|
|
47
48
|
federation_url = "osg-htc.org"
|
|
48
49
|
path = "/igwn/ligo"
|
|
49
50
|
interval = 600
|
|
51
|
+
jitter = 60
|
|
50
52
|
credential = "pelican_token"
|
|
51
53
|
cachefile = "/var/lib/gwdatafind/pelican-ligo.db"
|
|
52
54
|
|
|
@@ -57,6 +59,7 @@ type = "pelican"
|
|
|
57
59
|
federation_url = "osg-htc.org"
|
|
58
60
|
path = "/igwn/virgo"
|
|
59
61
|
interval = 600
|
|
62
|
+
jitter = 60
|
|
60
63
|
credential = "pelican_token"
|
|
61
64
|
cachefile = "/var/lib/gwdatafind/pelican-virgo.db"
|
|
62
65
|
|
|
@@ -67,6 +70,7 @@ type = "pelican"
|
|
|
67
70
|
federation_url = "osg-htc.org"
|
|
68
71
|
path = "/igwn/kagra"
|
|
69
72
|
interval = 600
|
|
73
|
+
jitter = 60
|
|
70
74
|
credential = "pelican_token"
|
|
71
75
|
cachefile = "/var/lib/gwdatafind/pelican-kagra.db"
|
|
72
76
|
|
|
@@ -30,6 +30,7 @@ services:
|
|
|
30
30
|
gwdatafind:
|
|
31
31
|
container_name: gwdatafind-server
|
|
32
32
|
image: containers.ligo.org/computing/gwdatafind/server:latest
|
|
33
|
+
image: ${GWDATAFIND_SERVER_IMAGE-containers.ligo.org/computing/gwdatafind/server:latest}
|
|
33
34
|
ports:
|
|
34
35
|
- "8080:8080"
|
|
35
36
|
restart: unless-stopped
|
|
@@ -45,9 +46,12 @@ services:
|
|
|
45
46
|
# Volumes
|
|
46
47
|
volumes:
|
|
47
48
|
- type: bind
|
|
48
|
-
source: gwdatafind-server.toml
|
|
49
|
+
source: ${GWDATAFIND_SERVER_CONFIG-./gwdatafind-server.toml}
|
|
49
50
|
target: /etc/gwdatafind/gwdatafind-server.toml
|
|
50
51
|
read_only: true
|
|
52
|
+
- type: volume
|
|
53
|
+
source: gwdatafind-data
|
|
54
|
+
target: /var/lib/gwdatafind
|
|
51
55
|
tmpfs:
|
|
52
56
|
- /tmp:noexec,nosuid,size=100m
|
|
53
57
|
- /var/tmp:noexec,nosuid,size=10m
|
|
@@ -69,3 +73,8 @@ services:
|
|
|
69
73
|
networks:
|
|
70
74
|
gwdatafind_internal:
|
|
71
75
|
driver: bridge
|
|
76
|
+
|
|
77
|
+
# Volumes
|
|
78
|
+
volumes:
|
|
79
|
+
gwdatafind-data:
|
|
80
|
+
driver: local
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# Copyright (c) 2019-2025 Cardiff University
|
|
2
2
|
# Licensed under GPLv3+ - see LICENSE
|
|
3
3
|
|
|
4
|
-
"""
|
|
5
|
-
data discover mechanism for data files.
|
|
4
|
+
"""A metadata-based discovery mechanism for GW data files."""
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
11
9
|
|
|
12
10
|
try:
|
|
13
11
|
from ._version import version as __version__
|
|
@@ -17,9 +15,17 @@ except ModuleNotFoundError: # development mode
|
|
|
17
15
|
from .app import GWDataFindApp
|
|
18
16
|
from .config import get_config_path
|
|
19
17
|
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
20
21
|
|
|
21
|
-
def create_app(
|
|
22
|
+
def create_app(
|
|
23
|
+
name: str = __name__,
|
|
24
|
+
config: Path | None = None,
|
|
25
|
+
app_class: type[GWDataFindApp] = GWDataFindApp,
|
|
26
|
+
**app_kwargs,
|
|
27
|
+
):
|
|
22
28
|
"""Create an instance of the application."""
|
|
23
29
|
if config is None:
|
|
24
30
|
config = get_config_path()
|
|
25
|
-
return
|
|
31
|
+
return app_class(name, config, **app_kwargs)
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.3.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 3, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gffb5a6c3c'
|