diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..59b20cb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +*.pyc +**__pycache__** +/spil_hamlet_conf/data/testing/SPIL_PROJECTS/** +/spil_hamlet_conf/data/caches/** +**/wip/** +*_secret.py +.git* diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ae2deca --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +# +version: '0.1.1' +services: + restapi: + image: spil/spil + build: + dockerfile: ./spil_server/docker/Dockerfile + restart: on-failure + ports: + - "80:80" + + volumes: + - ./spil_hamlet_conf:/spil_conf + + # for development: edit the config will reload the app + environment: + - WATCHFILES_FORCE_POLLING=true + + # development options for config hot reload. + command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--reload", "--reload-dir", "/spil_conf"] + # https://www.uvicorn.org/settings/ \ No newline at end of file diff --git a/spil_server/client_server.md b/spil_server/client_server.md new file mode 100644 index 0000000..314a9f1 --- /dev/null +++ b/spil_server/client_server.md @@ -0,0 +1,43 @@ +# Spil network deployment + +Client / Server deployment of Spil is still experimental, and work in progress. + +## Server side Spil + +A fastapi powered Spil REST API is currently under development. + +It allows access to the Crud interface via a rest api. +- /find/{config}/{sid} +- /get/{config}/{sid} +- /write/{config}/{sid} + +## Client Side Spil + +A FindInSpilRest Finder is also in development. + +It is able to consume the Spil rest API. +This Finder can replace any other finder, and be used without any change in the code. + + +## Client-Server + +Spil can run both on client and server +With a server instance, serving the rest API. +And a client instance, consuming the rest API, for example used by the UI. + +To make this happen clients and servers just need to use different configs. + +Finders are interchangeable and connectable. +The spil_data_conf defines which Finder is used for which data type. + +#### Connectable Finders + +Some Finders call or use other Finders. +For example: +- FindInAll + Calls do_find on other finders, depending on a config. +- FindInCache + Caches the result of other Finders +- FindInSpilRest + Calls the Spil Rest API, which in turn calls Finders. + diff --git a/spil_server/docker/Dockerfile b/spil_server/docker/Dockerfile new file mode 100644 index 0000000..f0fc098 --- /dev/null +++ b/spil_server/docker/Dockerfile @@ -0,0 +1,27 @@ +# Must be built from spils' repo root folder +# Build: docker build -t spil/spil -f spil_server/docker/Dockerfile . +# Run with python: docker run -ti spil/spil python + +#FROM python:3.11 +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7 + +COPY ./spil_server/docker/requirements.txt /spil/requirements.txt +RUN pip install --no-cache-dir --upgrade -r /spil/requirements.txt + +# spil will contain spil +COPY ./spil /spil/spil + +# This is a placeholder for a later custom config (see docker-compose.yml) +RUN mkdir /spil_conf +ENV PYTHONPATH="${PYTHONPATH}:/spil:/spil_conf" + +# spil_hamlet_conf contains the spil demo config +COPY ./spil_hamlet_conf /spil/spil_hamlet_conf + +# Init test data +# PYTHONDONTWRITEBYTECODE avoids to have __pycache__ in the Image +RUN export PYTHONDONTWRITEBYTECODE=1 && python -c "import spil;import hamlet_scripts.save_examples_to_mock_fs as mfs;mfs.run()" + +# App will contain the fastapi app +# Starting app:main.py is already included in the base docker image (tiangolo/uvicorn-gunicorn-fastapi) +COPY ./spil_server/fastapi/app /app \ No newline at end of file diff --git a/spil_server/docker/requirements.txt b/spil_server/docker/requirements.txt new file mode 100644 index 0000000..8bcf010 --- /dev/null +++ b/spil_server/docker/requirements.txt @@ -0,0 +1,5 @@ +Fileseq +future +logzero +codetiming +typing-extensions \ No newline at end of file diff --git a/spil_server/fastapi/app/main.py b/spil_server/fastapi/app/main.py new file mode 100644 index 0000000..b4b70f3 --- /dev/null +++ b/spil_server/fastapi/app/main.py @@ -0,0 +1,65 @@ +""" +uvicorn main:app --reload +""" +from __future__ import annotations +from typing import Any, Optional, List + +from pathlib import Path +from fastapi import FastAPI +from starlette.requests import Request + +try: + import spil +except ImportError: + root = Path(__file__).resolve().parent.parent.parent.parent + import sys + sys.path.append(root.as_posix()) + +from spil import Sid, Finder, Getter, FindInList +from spil import FindInAll, GetFromAll, FindInPaths, GetFromPaths +# from spil_plugins.sg.get_sg import GetFromSG +# from spil_plugins.sg.find_sg import FindInSG +from spil_hamlet_conf.hamlet_scripts.example_sids import sids + +app = FastAPI() + +finder_config = { + # 'sg': FindInSG(), + 'all': FindInAll(), + 'paths': FindInPaths(), + 'ls': FindInList(list(sids)) +} + +getter_config = { + # 'sg': GetFromSG(), + 'all': GetFromAll(), + 'paths': GetFromPaths() +} + +@app.get("/find/{config}/{search:path}") +def find(config: str, search: str, request: Request): + finder: Finder | None = finder_config.get(config) + if not finder: + print(f"Finder not found for {config}") + return [] + + print(f"Finder: {finder}") + search = f"{search}{('?' + str(request.query_params)) if request.query_params else ''}" + print(f"{search}") + + for sid in finder.find(search): + yield {"sid": sid.uri} + +@app.get("/get/{config}/{search:path}") +def get(config: str, search: str, request: Request): + getter: Getter | None = getter_config.get(config) + if not getter: + print(f"Finder not found for {config}") + return [] + + print(f"Getter: {getter}") + search = f"{search}{('?' + str(request.query_params)) if request.query_params else ''}" + print(f"{search}") + + for data in getter.get(search): + yield data