Metadata-Version: 2.4
Name: ebsclient
Version: 0.1.1
Summary: Ephemeral buffer and Microservices API client
Author-email: Benjamin De Zordo <benjamin.de-zordo@irisa.fr>
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: System :: Distributed Computing
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: requests
Requires-Dist: ebcommon>=1.0.0
Requires-Dist: remotemanager<=0.14.3
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"

# ebsclient

Client Python pour les services **ebuffer**, **ebservice** et **ebmessage**.

`ebsclient` fournit deux niveaux d'utilisation :

- **APIs** : une représentation directe des endpoints HTTP de chaque service
  (`BufferAPI`, `RuntimeAPI`, `MicroserviceAPI`, `MessageAPI`, `PolicyAPI`).
- **Wrappers** : des objets Python (`BufferWrapper`, `JobWrapper`,
  `RuntimeWrapper`, `MicroserviceWrapper`, `PolicyWrapper`) qui exposent des
  méthodes appelables directement sur les objets renvoyés par l'API, ainsi que
  la couche d'orchestration `RuntimeService` / `RuntimeJob` côté mainteneur.

## Installation

La bibliothèque n'est, pour le moment, pas publiée sur PyPI. Elle s'installe en mode développement :

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ./ebsclient_package/
```

En cas de problème de cache :

```bash
pip uninstall ebsclient
pip cache purge
```

## Démarrage rapide

```python
from ebsclient import EbClientAPI

# Authentification par token
api = EbClientAPI(
    ebservice_url="https://ebservice.dormain.org/api/v1",
    ebuffer_url="https://ebuffer.dormain.org/api/v1",
    token="mon_token",
)

# ... ou par identifiants
api = EbClientAPI(
    ebservice_url="https://ebservice.dormain.org/api/v1",
    ebuffer_url="https://ebuffer.dormain.org/api/v1",
    username="maintainer",
    password="maintainer_password",
)

# Lister les microservices et manipuler les objets directement
for ms in api.mservices.list():
    print(ms.uuid)
    print(ms.get_tags())
    ms.add_tag("readme_test")
    ms.show()
```

> [!note]
> `ebservice_url` et `ebuffer_url` sont **obligatoires**. `ebmessage_url` est
> optionnel : il n'est requis que si vous utilisez l'envoi de messages
> (`api.messages.send(...)`).

## Configuration du client

`EbClientAPI` accepte les paramètres suivants :

| Paramètre       | Obligatoire | Description                                              |
|-----------------|-------------|----------------------------------------------------------|
| `ebservice_url` | oui         | URL de base du service ebservice                         |
| `ebuffer_url`   | oui         | URL de base du service ebuffer                           |
| `ebmessage_url` | non         | URL de base du service ebmessage (envoi de messages)     |
| `token`         | non*        | Token d'accès personnel                                  |
| `username`      | non*        | Nom d'utilisateur                                        |
| `password`      | non*        | Mot de passe                                             |
| `timeout`       | non         | Timeout HTTP en secondes (défaut : 30)                   |

\* Il faut fournir **soit** un `token`, **soit** un couple `username` + `password`.

Le client crée une `requests.Session` partagée avec retries automatiques (sur les
codes 502/503/504, hors POST pour éviter les doublons) et applique le timeout et
les en-têtes d'authentification à toutes les requêtes via un point d'entrée unique.

## Architecture

```
ebsclient/
├── client.py              : EbClientAPI (point d'entrée, authentification, session HTTP)
├── apis/
│   ├── buffer_api.py       : représentation Python de l'API ebuffer
│   ├── message_api.py      : représentation Python de l'API ebmessage
│   ├── microservice_api.py : représentation Python de l'API microservices
│   ├── policy_api.py       : représentation Python de l'API policies
│   └── runtime_api.py      : représentation Python de l'API runtimes
├── utils/
│   └── validators.py       : fonctions de validation (uuid, email, bornes, ...)
└── wrappers/
    ├── buffer.py           : BufferWrapper        (ebuffer en tant qu'objet)
    ├── job.py              : JobWrapper           (job en tant qu'objet)
    ├── microservice.py     : MicroserviceWrapper  (microservice en tant qu'objet)
    ├── policy.py           : PolicyWrapper        (policy en tant qu'objet)
    ├── runtime.py          : RuntimeWrapper       (runtime en tant qu'objet)
    └── runtime_service.py  : RuntimeService / RuntimeRunner / RuntimeJob
                              (couche d'orchestration, usage mainteneur)
```

### APIs vs Wrappers

Les méthodes des **APIs** prennent des identifiants (uuid) et renvoient des
wrappers :

```python
ms = api.mservices.get("dca80297-b1ad-4e3e-8b76-c6a11c95aab2")  # -> MicroserviceWrapper
```

Les **wrappers** délèguent de façon transparente aux attributs de l'objet
sous-jacent (via `__getattr__`) et ajoutent des méthodes pratiques :

```python
ms.name            # attribut délégué à l'objet Microservice
ms.get_tags()      # méthode du wrapper
ms.add_tag("x")    # méthode du wrapper, rafraîchit l'objet
ms.refresh()       # recharge l'objet depuis le serveur
ms.show()          # affiche les détails
```

### Couche d'orchestration (mainteneur)

`runtime_service.py` permet à un mainteneur de faire tourner un runtime qui
écoute ses microservices et exécute les jobs en attente, chacun dans son propre
thread :

- `RuntimeService` : surveille un runtime et lance un `RuntimeRunner` par
  microservice associé.
- `RuntimeRunner`  : surveille un microservice et lance un thread par job en
  attente.
- `RuntimeJob`     : exécute un job (récupération des entrées, exécution, envoi
  des sorties). Classe destinée à être **héritée** en surchargeant les hooks
  d'entrée/sortie et la méthode `execute`.

> Pour une utilisation simplifiée de cette couche (templates HPC/Slurm), voir la
> bibliothèque **ebstemplate**.

## Tests

Les tests sont séparés selon ce dont ils ont besoin :

```bash
pip install pytest

# Tests hors-ligne (aucun serveur requis) : validators, client, runtime_service
pytest ebsclient_package/tests/unit ebsclient_package/tests/wrapper/test_runtime_service.py -v

# Tous les tests (nécessite des serveurs ebservice/ebuffer locaux)
pytest ebsclient_package/tests/ -v

# Un fichier spécifique
pytest ebsclient_package/tests/api/test_buffer_api.py -v

# Avec affichage des logs
pytest ebsclient_package/tests/ -v -s
```

Voir [`ebsclient_package/tests/README.md`](./tests/README.md) pour le détail de l'organisation des tests.
