From b667c1616c9a5acfcbd2048535c8b619905e1f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 15:55:24 +0100 Subject: [PATCH 01/25] initial commit --- server/Dockerfile | 15 +++++++++++++++ server/app/main.py | 4 ++++ server/nginx-body-buffer-size.conf | 6 ++++++ 3 files changed, 25 insertions(+) create mode 100644 server/Dockerfile create mode 100644 server/app/main.py create mode 100644 server/nginx-body-buffer-size.conf diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 00000000..7a31ff21 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,15 @@ +FROM tiangolo/uwsgi-nginx:python3.11 + +# Should match client_body_buffer_size defined in nginx-body-buffer-size.conf +ENV NGINX_MAX_UPLOAD 1M + +# object stores aren't thread-safe yet +# https://github.com/eclipse-basyx/basyx-python-sdk/issues/205 +ENV UWSGI_CHEAPER 0 +ENV UWSGI_PROCESSES 1 + +RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@feature/http_api + +COPY ./app /app + +COPY ./nginx-body-buffer-size.conf /etc/nginx/conf.d/body-buffer-size.conf diff --git a/server/app/main.py b/server/app/main.py new file mode 100644 index 00000000..cf5714a5 --- /dev/null +++ b/server/app/main.py @@ -0,0 +1,4 @@ +from basyx.aas.backend.local_file import LocalFileObjectStore +from basyx.aas.adapter.http import WSGIApp + +application = WSGIApp(LocalFileObjectStore("/storage")) diff --git a/server/nginx-body-buffer-size.conf b/server/nginx-body-buffer-size.conf new file mode 100644 index 00000000..423e8cd6 --- /dev/null +++ b/server/nginx-body-buffer-size.conf @@ -0,0 +1,6 @@ +# While the Dockerfile sets client_max_body_size, it doesn't +# allow us to define client_body_buffer_size, which is 16k +# by default. This results in temporary buffer files being +# created, in case the request body exceeds this limit. Thus, +# we override it here to match client_max_body_size. +client_body_buffer_size 1M; From 65b07969efbdfbbd7de790b431a3b3a6b4e1b267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:42:58 +0100 Subject: [PATCH 02/25] add readme --- server/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 server/README.md diff --git a/server/README.md b/server/README.md new file mode 100644 index 00000000..afbdcad3 --- /dev/null +++ b/server/README.md @@ -0,0 +1,36 @@ +# Eclipse BaSyx Python SDK - HTTP Server + +This repository contains a Dockerfile to spin up an exemplary DotAAS HTTP/REST server with ease. +The server currently implements the following interfaces: + +- [Asset Administration Shell Repository Service][4] +- [Submodel Repository Service][5] + +It uses the [HTTP Adapter][1] and the [Local-File Backend][2] of the [BaSyx Python SDK][3], which allows server-side data to be stored persistently across container restarts. + + +## Building +The container image can be built via: +``` +$ docker build -t basyx-python-sdk-http-server . +``` + +## Running +Because the server uses the [Local-File Backend][2], the container needs to be provided with the directory `/storage` to store AAS and Submodel JSON files. +This directory can be mapped via the `-v` option from another image or a local directory. +To map the directory `storage` inside the container, `-v ./storage:/storage` can be used. +The directory `storage` will be created in the current working directory, if it doesn't already exist. + +The HTTP server inside the container listens on port 80 by default. +To expose it on the host on port 8080, use the option `-p 8080:80` when running it. + +Putting it all together, the container can be started via the following command: +``` +$ docker run -p 8080:80 -v ./storage:/storage basyx-python-sdk-http-server +``` + +[1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 +[2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html +[3]: https://github.com/eclipse-basyx/basyx-python-sdk +[4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.0.1_SSP-001 +[5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 From 38303efa1d0042a0ab337064d279582f2c96c01a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:51:13 +0100 Subject: [PATCH 03/25] move nginx config to subdirectory --- server/Dockerfile | 3 +-- .../body-buffer-size.conf} | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename server/{nginx-body-buffer-size.conf => nginx/body-buffer-size.conf} (100%) diff --git a/server/Dockerfile b/server/Dockerfile index 7a31ff21..ac4c1cb0 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -11,5 +11,4 @@ ENV UWSGI_PROCESSES 1 RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@feature/http_api COPY ./app /app - -COPY ./nginx-body-buffer-size.conf /etc/nginx/conf.d/body-buffer-size.conf +COPY ./nginx /etc/nginx/conf.d diff --git a/server/nginx-body-buffer-size.conf b/server/nginx/body-buffer-size.conf similarity index 100% rename from server/nginx-body-buffer-size.conf rename to server/nginx/body-buffer-size.conf From 73bf9e8df336baf1d9352947ae1dea6a4696e121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:51:45 +0100 Subject: [PATCH 04/25] `docker build` is deprecated --- server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/README.md b/server/README.md index afbdcad3..e387bc49 100644 --- a/server/README.md +++ b/server/README.md @@ -12,7 +12,7 @@ It uses the [HTTP Adapter][1] and the [Local-File Backend][2] of the [BaSyx Pyth ## Building The container image can be built via: ``` -$ docker build -t basyx-python-sdk-http-server . +$ docker buildx build -t basyx-python-sdk-http-server . ``` ## Running From 46545f693f8752fa3ed591eed04e0239427b84c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:57:17 +0100 Subject: [PATCH 05/25] Dockerfile: update a comment The nginx config has been moved in 38303efa1d0042a0ab337064d279582f2c96c01a. --- server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Dockerfile b/server/Dockerfile index ac4c1cb0..e6e65e21 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,6 +1,6 @@ FROM tiangolo/uwsgi-nginx:python3.11 -# Should match client_body_buffer_size defined in nginx-body-buffer-size.conf +# Should match client_body_buffer_size defined in nginx/body-buffer-size.conf ENV NGINX_MAX_UPLOAD 1M # object stores aren't thread-safe yet From e9ee8b06f24e01942b07b41d414a5211bb981762 Mon Sep 17 00:00:00 2001 From: s-heppner Date: Mon, 1 Apr 2024 10:39:39 +0200 Subject: [PATCH 06/25] README.md: Add link to API specification --- server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/README.md b/server/README.md index e387bc49..78b9aba6 100644 --- a/server/README.md +++ b/server/README.md @@ -1,6 +1,6 @@ # Eclipse BaSyx Python SDK - HTTP Server -This repository contains a Dockerfile to spin up an exemplary DotAAS HTTP/REST server with ease. +This repository contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API](https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces) with ease. The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] From 9f4b9ca77ebd5a6993d1e04d1bd62bcf66929de6 Mon Sep 17 00:00:00 2001 From: jkhsjdhjs Date: Thu, 13 Jun 2024 16:08:52 +0200 Subject: [PATCH 07/25] readme: add note about windows paths --- server/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/README.md b/server/README.md index 78b9aba6..fc01651d 100644 --- a/server/README.md +++ b/server/README.md @@ -29,6 +29,11 @@ Putting it all together, the container can be started via the following command: $ docker run -p 8080:80 -v ./storage:/storage basyx-python-sdk-http-server ``` +Since Windows uses backslashes instead of forward slashes in paths, you'll have to adjust the path to the storage directory there: +``` +> docker run -p 8080:80 -v .\storage:/storage basyx-python-sdk-http-server +``` + [1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 [2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html [3]: https://github.com/eclipse-basyx/basyx-python-sdk From 7fe62b44d19e4ee6ad16872fbc0166a49df3169c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Fri, 14 Jun 2024 01:17:29 +0200 Subject: [PATCH 08/25] readme: move a link to the bottom --- server/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/README.md b/server/README.md index fc01651d..507c9d3b 100644 --- a/server/README.md +++ b/server/README.md @@ -1,6 +1,6 @@ # Eclipse BaSyx Python SDK - HTTP Server -This repository contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API](https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces) with ease. +This repository contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API][6] with ease. The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] @@ -39,3 +39,4 @@ Since Windows uses backslashes instead of forward slashes in paths, you'll have [3]: https://github.com/eclipse-basyx/basyx-python-sdk [4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.0.1_SSP-001 [5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 +[6]: https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces From dd923301f71e3cc84844e2dd0a035091c7785c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sun, 16 Jun 2024 01:12:33 +0200 Subject: [PATCH 09/25] nginx: add CORS header --- server/nginx/cors-header.conf | 1 + 1 file changed, 1 insertion(+) create mode 100644 server/nginx/cors-header.conf diff --git a/server/nginx/cors-header.conf b/server/nginx/cors-header.conf new file mode 100644 index 00000000..af78db3a --- /dev/null +++ b/server/nginx/cors-header.conf @@ -0,0 +1 @@ +add_header Access-Control-Allow-Origin *; From 1f62df939def790dd652ee14134caa16abcfc18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sun, 16 Jun 2024 01:17:40 +0200 Subject: [PATCH 10/25] app: support AASX files This makes the server read AASX files by default, as it seems to be the most common usecase. Furthermore, the user can now configure the container via environment variables. These options are also documented in the README. --- server/README.md | 14 +++++++++++++- server/app/main.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/server/README.md b/server/README.md index 507c9d3b..090f7631 100644 --- a/server/README.md +++ b/server/README.md @@ -6,8 +6,11 @@ The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] - [Submodel Repository Service][5] -It uses the [HTTP Adapter][1] and the [Local-File Backend][2] of the [BaSyx Python SDK][3], which allows server-side data to be stored persistently across container restarts. +It uses the [HTTP API][1] and the [AASX Adapter][7] of the [BaSyx Python SDK][3], to serve AASX files from a given directory. +The AASX files are only read, chages won't persist. +Alternatively, the container can also be told to use the [Local-File Backend][2] instead, which stores AAS and Submodels as individual JSON files and allows for persistent changes (except supplementary files, i.e. files referenced by `File` submodel elements). +See [below](#options) on how to configure this. ## Building The container image can be built via: @@ -34,9 +37,18 @@ Since Windows uses backslashes instead of forward slashes in paths, you'll have > docker run -p 8080:80 -v .\storage:/storage basyx-python-sdk-http-server ``` +## Options +The container can be configured via environment variables: +- `API_BASE_PATH` determines the base path under which all other API paths are made available. Default: `/api/v3.0` +- `STORAGE_TYPE` can be one of `AASX` or `LOCAL_FILE`: + - When set to `AASX` (the default), the server will read and serve AASX files from the storage directory. The AASX files are not modified, all changes done via the API are only stored in memory. + - When instead set to `LOCAL_FILE`, the server makes use of the [LocalFileBackend][2], where AAS and Submodels are persistently stored as JSON files. Supplementary files, i.e. files referenced by `File` submodel elements, are not stored in this case. +- `STORAGE_PATH` sets the directory to read the files from *within the container*. If you bind your files to a directory different from the default `/storage`, you can use this variable to adjust the server accordingly. + [1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 [2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html [3]: https://github.com/eclipse-basyx/basyx-python-sdk [4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.0.1_SSP-001 [5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 [6]: https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces +[7]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/aasx.html#adapter-aasx diff --git a/server/app/main.py b/server/app/main.py index cf5714a5..9e9488ed 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -1,4 +1,37 @@ +import os +import pathlib +import sys + +from basyx.aas import model +from basyx.aas.adapter import aasx + from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -application = WSGIApp(LocalFileObjectStore("/storage")) +storage_path = os.getenv("STORAGE_PATH", "/storage") +storage_type = os.getenv("STORAGE_TYPE", "AASX") +base_path = os.getenv("API_BASE_PATH") + +wsgi_optparams = {} + +if base_path is not None: + wsgi_optparams["base_path"] = base_path + +if storage_type == "LOCAL_FILE": + application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) + +elif storage_type == "AASX": + object_store: model.DictObjectStore = model.DictObjectStore() + file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() + + for file in pathlib.Path(storage_path).glob("*.aasx"): + if not file.is_file(): + continue + print(f"Loading {file}") + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) + + application = WSGIApp(object_store, file_store, **wsgi_optparams) + +else: + print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) From 31f9ea35dbbdeb2d73e942faed4ec28c8f30d910 Mon Sep 17 00:00:00 2001 From: Igor Garmaev <56840636+zrgt@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:18:15 +0200 Subject: [PATCH 11/25] app: support JSON and XML files This makes the server also read JSON and XML files --- server/app/main.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index 9e9488ed..302bc5fc 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -2,13 +2,13 @@ import pathlib import sys -from basyx.aas import model +from basyx.aas import model, adapter from basyx.aas.adapter import aasx from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -storage_path = os.getenv("STORAGE_PATH", "/storage") +storage_path = os.getenv("STORAGE_PATH", "storage") storage_type = os.getenv("STORAGE_TYPE", "AASX") base_path = os.getenv("API_BASE_PATH") @@ -20,18 +20,27 @@ if storage_type == "LOCAL_FILE": application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) -elif storage_type == "AASX": +elif storage_type in ("AASX", "JSON", "XML"): object_store: model.DictObjectStore = model.DictObjectStore() file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() - for file in pathlib.Path(storage_path).glob("*.aasx"): + suffix_pattern = f"*.{storage_type.lower()}" + for file in pathlib.Path(storage_path).glob(suffix_pattern): if not file.is_file(): continue print(f"Loading {file}") - with aasx.AASXReader(file) as reader: - reader.read_into(object_store=object_store, file_store=file_store) + + if storage_type == "JSON": + with open(file) as f: + adapter.json.read_aas_json_file_into(object_store, f) + elif storage_type == "XML": + with open(file) as f: + adapter.xml.read_aas_xml_file_into(object_store, file) + else: + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) application = WSGIApp(object_store, file_store, **wsgi_optparams) else: - print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) + print(f"STORAGE_TYPE must be either LOCAL_FILE, AASX, JSON or XML! Current value: {storage_type}", file=sys.stderr) From b32c97cd4b0187958c7a55b788b21a2f601524c2 Mon Sep 17 00:00:00 2001 From: zrgt Date: Wed, 21 Aug 2024 23:34:19 +0200 Subject: [PATCH 12/25] Revert "app: support JSON and XML files " This reverts commit 31f9ea35dbbdeb2d73e942faed4ec28c8f30d910. --- server/app/main.py | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index 302bc5fc..9e9488ed 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -2,13 +2,13 @@ import pathlib import sys -from basyx.aas import model, adapter +from basyx.aas import model from basyx.aas.adapter import aasx from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -storage_path = os.getenv("STORAGE_PATH", "storage") +storage_path = os.getenv("STORAGE_PATH", "/storage") storage_type = os.getenv("STORAGE_TYPE", "AASX") base_path = os.getenv("API_BASE_PATH") @@ -20,27 +20,18 @@ if storage_type == "LOCAL_FILE": application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) -elif storage_type in ("AASX", "JSON", "XML"): +elif storage_type == "AASX": object_store: model.DictObjectStore = model.DictObjectStore() file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() - suffix_pattern = f"*.{storage_type.lower()}" - for file in pathlib.Path(storage_path).glob(suffix_pattern): + for file in pathlib.Path(storage_path).glob("*.aasx"): if not file.is_file(): continue print(f"Loading {file}") - - if storage_type == "JSON": - with open(file) as f: - adapter.json.read_aas_json_file_into(object_store, f) - elif storage_type == "XML": - with open(file) as f: - adapter.xml.read_aas_xml_file_into(object_store, file) - else: - with aasx.AASXReader(file) as reader: - reader.read_into(object_store=object_store, file_store=file_store) + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) application = WSGIApp(object_store, file_store, **wsgi_optparams) else: - print(f"STORAGE_TYPE must be either LOCAL_FILE, AASX, JSON or XML! Current value: {storage_type}", file=sys.stderr) + print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) From 73569bfb4b22b099310d7909f8718582418f57e6 Mon Sep 17 00:00:00 2001 From: zrgt Date: Wed, 21 Aug 2024 23:39:45 +0200 Subject: [PATCH 13/25] app: Add support of JSON and XML files - This commit adds support for loading AASX, JSON or XML files. So the user can have a mix of these files in the storage folder and the application will load them all. - Changed types of storage to LOCAL_FILE_BACKEND and LOCAL_FILE_READ_ONLY - Adapted and refactored README.md and added more examples for running --- server/README.md | 37 +++++++++++++++++++++++++++---------- server/app/main.py | 27 ++++++++++++++++++--------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/server/README.md b/server/README.md index 090f7631..853f45ef 100644 --- a/server/README.md +++ b/server/README.md @@ -6,8 +6,8 @@ The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] - [Submodel Repository Service][5] -It uses the [HTTP API][1] and the [AASX Adapter][7] of the [BaSyx Python SDK][3], to serve AASX files from a given directory. -The AASX files are only read, chages won't persist. +It uses the [HTTP API][1] and the [AASX][7], [JSON][8], and [XML][9] Adapters of the [BaSyx Python SDK][3], to serve regarding files from a given directory. +The files are only read, chages won't persist. Alternatively, the container can also be told to use the [Local-File Backend][2] instead, which stores AAS and Submodels as individual JSON files and allows for persistent changes (except supplementary files, i.e. files referenced by `File` submodel elements). See [below](#options) on how to configure this. @@ -19,14 +19,31 @@ $ docker buildx build -t basyx-python-sdk-http-server . ``` ## Running -Because the server uses the [Local-File Backend][2], the container needs to be provided with the directory `/storage` to store AAS and Submodel JSON files. + +### Storage +The container needs to be provided with the directory `/storage` to store AAS and Submodel files: AASX, JSON, XML or JSON files of Local-File Backend. + This directory can be mapped via the `-v` option from another image or a local directory. To map the directory `storage` inside the container, `-v ./storage:/storage` can be used. The directory `storage` will be created in the current working directory, if it doesn't already exist. +### Port The HTTP server inside the container listens on port 80 by default. To expose it on the host on port 8080, use the option `-p 8080:80` when running it. +### Options +The container can be configured via environment variables: +- `API_BASE_PATH` determines the base path under which all other API paths are made available. + Default: `/api/v3.0` +- `STORAGE_TYPE` can be one of `LOCAL_FILE_READ_ONLY` or `LOCAL_FILE_BACKEND`: + - When set to `LOCAL_FILE_READ_ONLY` (the default), the server will read and serve AASX, JSON, XML files from the storage directory. + The files are not modified, all changes done via the API are only stored in memory. + - When instead set to `LOCAL_FILE`, the server makes use of the [LocalFileBackend][2], where AAS and Submodels are persistently stored as JSON files. + Supplementary files, i.e. files referenced by `File` submodel elements, are not stored in this case. +- `STORAGE_PATH` sets the directory to read the files from *within the container*. If you bind your files to a directory different from the default `/storage`, you can use this variable to adjust the server accordingly. + +### Running Examples + Putting it all together, the container can be started via the following command: ``` $ docker run -p 8080:80 -v ./storage:/storage basyx-python-sdk-http-server @@ -37,13 +54,11 @@ Since Windows uses backslashes instead of forward slashes in paths, you'll have > docker run -p 8080:80 -v .\storage:/storage basyx-python-sdk-http-server ``` -## Options -The container can be configured via environment variables: -- `API_BASE_PATH` determines the base path under which all other API paths are made available. Default: `/api/v3.0` -- `STORAGE_TYPE` can be one of `AASX` or `LOCAL_FILE`: - - When set to `AASX` (the default), the server will read and serve AASX files from the storage directory. The AASX files are not modified, all changes done via the API are only stored in memory. - - When instead set to `LOCAL_FILE`, the server makes use of the [LocalFileBackend][2], where AAS and Submodels are persistently stored as JSON files. Supplementary files, i.e. files referenced by `File` submodel elements, are not stored in this case. -- `STORAGE_PATH` sets the directory to read the files from *within the container*. If you bind your files to a directory different from the default `/storage`, you can use this variable to adjust the server accordingly. +Per default, the server will use the `LOCAL_FILE_READ_ONLY` storage type and serve the API under `/api/v3.0` and read files from `/storage`. If you want to change this, you can do so like this: +``` +$ docker run -p 8080:80 -v ./storage2:/storage2 -e API_BASE_PATH=/api/v3.1 -e STORAGE_TYPE=LOCAL_FILE_BACKEND -e STORAGE_PATH=/storage2 basyx-python-sdk-http-server +``` + [1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 [2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html @@ -52,3 +67,5 @@ The container can be configured via environment variables: [5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 [6]: https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces [7]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/aasx.html#adapter-aasx +[8]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/json.html +[9]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/xml.html diff --git a/server/app/main.py b/server/app/main.py index 9e9488ed..715fa631 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -2,14 +2,14 @@ import pathlib import sys -from basyx.aas import model +from basyx.aas import model, adapter from basyx.aas.adapter import aasx from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -storage_path = os.getenv("STORAGE_PATH", "/storage") -storage_type = os.getenv("STORAGE_TYPE", "AASX") +storage_path = os.getenv("STORAGE_PATH", "storage") +storage_type = os.getenv("STORAGE_TYPE", "LOCAL_FILE_READ_ONLY") base_path = os.getenv("API_BASE_PATH") wsgi_optparams = {} @@ -17,21 +17,30 @@ if base_path is not None: wsgi_optparams["base_path"] = base_path -if storage_type == "LOCAL_FILE": +if storage_type == "LOCAL_FILE_BACKEND": application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) -elif storage_type == "AASX": +elif storage_type in "LOCAL_FILE_READ_ONLY": object_store: model.DictObjectStore = model.DictObjectStore() file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() - for file in pathlib.Path(storage_path).glob("*.aasx"): + for file in pathlib.Path(storage_path).iterdir(): if not file.is_file(): continue print(f"Loading {file}") - with aasx.AASXReader(file) as reader: - reader.read_into(object_store=object_store, file_store=file_store) + + if file.suffix == ".json": + with open(file) as f: + adapter.json.read_aas_json_file_into(object_store, f) + elif file.suffix == ".xml": + with open(file) as f: + adapter.xml.read_aas_xml_file_into(object_store, file) + elif file.suffix == ".aasx": + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) application = WSGIApp(object_store, file_store, **wsgi_optparams) else: - print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) + print(f"STORAGE_TYPE must be either LOCAL_FILE or LOCAL_FILE_READ_ONLY! Current value: {storage_type}", + file=sys.stderr) From f8c81ba4824f2d3704a66f8ed773a8c87510d747 Mon Sep 17 00:00:00 2001 From: zrgt Date: Mon, 2 Sep 2024 16:36:13 +0200 Subject: [PATCH 14/25] app: lower the suffixes Lower the suffixes to also match the filenames with uppercase suffixes --- server/app/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index 715fa631..c8d60aff 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -29,13 +29,13 @@ continue print(f"Loading {file}") - if file.suffix == ".json": + if file.suffix.lower() == ".json": with open(file) as f: adapter.json.read_aas_json_file_into(object_store, f) - elif file.suffix == ".xml": + elif file.suffix.lower() == ".xml": with open(file) as f: adapter.xml.read_aas_xml_file_into(object_store, file) - elif file.suffix == ".aasx": + elif file.suffix.lower() == ".aasx": with aasx.AASXReader(file) as reader: reader.read_into(object_store=object_store, file_store=file_store) @@ -44,3 +44,6 @@ else: print(f"STORAGE_TYPE must be either LOCAL_FILE or LOCAL_FILE_READ_ONLY! Current value: {storage_type}", file=sys.stderr) + +# Lower the suffix +# Lower the suffixes to also match the filenames with uppercase suffixes \ No newline at end of file From 6ad831fece14541d448c47836ca6e6b71eb958ff Mon Sep 17 00:00:00 2001 From: zrgt Date: Tue, 3 Sep 2024 18:17:31 +0200 Subject: [PATCH 15/25] Remove comments --- server/app/main.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index c8d60aff..6662c868 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -44,6 +44,3 @@ else: print(f"STORAGE_TYPE must be either LOCAL_FILE or LOCAL_FILE_READ_ONLY! Current value: {storage_type}", file=sys.stderr) - -# Lower the suffix -# Lower the suffixes to also match the filenames with uppercase suffixes \ No newline at end of file From bfbf67727a2259f0ffc2f288ac625ffe6895b488 Mon Sep 17 00:00:00 2001 From: s-heppner Date: Wed, 25 Sep 2024 09:21:56 +0200 Subject: [PATCH 16/25] Dockerfile: Update branch Previously, the Dockerfile pointed to the development branch of the HTTP Server. This is outdated and the branch has been merged with main. Therefore, we point the Dockerfile to the main branch. --- server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Dockerfile b/server/Dockerfile index e6e65e21..5361ad2c 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -8,7 +8,7 @@ ENV NGINX_MAX_UPLOAD 1M ENV UWSGI_CHEAPER 0 ENV UWSGI_PROCESSES 1 -RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@feature/http_api +RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@main COPY ./app /app COPY ./nginx /etc/nginx/conf.d From 32c35b9fc72201bac4e22a37e2873829847a4dac Mon Sep 17 00:00:00 2001 From: otto-ifak <87818460+otto-ifak@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:57:54 +0200 Subject: [PATCH 17/25] Add docker compose file (#6) This commit adds a docker compose file for easier deployment. --------- Co-authored-by: s-heppner --- server/.gitignore | 1 + server/compose.yml | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 server/.gitignore create mode 100644 server/compose.yml diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 00000000..34ff768e --- /dev/null +++ b/server/.gitignore @@ -0,0 +1 @@ +storage/ diff --git a/server/compose.yml b/server/compose.yml new file mode 100644 index 00000000..263a9519 --- /dev/null +++ b/server/compose.yml @@ -0,0 +1,7 @@ +services: + app: + build: . + ports: + - 8080:80 + volumes: + - ./storage:/app/storage From 1e6e4e4dccd4e354775ad165f6c60d0104f5d580 Mon Sep 17 00:00:00 2001 From: Frosty2500 <125310380+Frosty2500@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:58:45 +0200 Subject: [PATCH 18/25] app/main.py: Fix default storage_path (#7) Previously, trying to run the container using the tutorial from the `README.md` led to an error, due to a wrongly set default storage path if the environment variable `STORAGE_PATH` is not set. This fixes this issue by setting the default storage path to `/storage` rather than the prior relative path: `storage`. --- server/app/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app/main.py b/server/app/main.py index 6662c868..c502bfbe 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -8,7 +8,7 @@ from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -storage_path = os.getenv("STORAGE_PATH", "storage") +storage_path = os.getenv("STORAGE_PATH", "/storage") storage_type = os.getenv("STORAGE_TYPE", "LOCAL_FILE_READ_ONLY") base_path = os.getenv("API_BASE_PATH") From 4078a0a3cb08d6a5e66295978324e54fa528cb43 Mon Sep 17 00:00:00 2001 From: s-heppner Date: Mon, 21 Oct 2024 08:57:47 +0200 Subject: [PATCH 19/25] Dockerfile: Change the path to the main Eclipse BaSyx Python SDK repository Previously, we used the `rwth-iat` fork to pull the SDK from, inside the docker file, due to the fact, that the `eclipse-basyx` repository, did not contain the necessary branch. Now that this is the case, it makes sense to always use the official repository. This also fixes the dependency issues of missing dependencies, as they have been fixed in the main repository. Fix #8 Fix #9 --- server/.gitignore | 4 ++++ server/Dockerfile | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/server/.gitignore b/server/.gitignore index 34ff768e..af40b724 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1 +1,5 @@ +# Ignore the default AAS storage directory storage/ + +# Ignore PyCharm IDE configuration +.idea diff --git a/server/Dockerfile b/server/Dockerfile index 5361ad2c..7ed81b4d 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -8,7 +8,7 @@ ENV NGINX_MAX_UPLOAD 1M ENV UWSGI_CHEAPER 0 ENV UWSGI_PROCESSES 1 -RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@main +RUN pip install --no-cache-dir git+https://github.com/eclipse-basyx/basyx-python-sdk@main COPY ./app /app COPY ./nginx /etc/nginx/conf.d From 4da7f140be368db3685460e52b1e7e3ca04ca817 Mon Sep 17 00:00:00 2001 From: s-heppner Date: Mon, 4 Nov 2024 15:20:30 +0100 Subject: [PATCH 20/25] Adapt Dockerfile to new BaSyx-Python SDK repository structure Previously, the Eclipse BaSyx-Python SDK contained just the SDK module. With the recent refactoring of the SDK GitHub repository towards a monolithic structure, the SDK module moved into the `./sdk` subdirectory, see [basyx-python-sdk#317]. With the new repository structure, we obviously have to adapt the install path of the SDK in this Dockerfile, which has been done here. [basyx-python-sdk#317]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/317 --- server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Dockerfile b/server/Dockerfile index 7ed81b4d..92542120 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -8,7 +8,7 @@ ENV NGINX_MAX_UPLOAD 1M ENV UWSGI_CHEAPER 0 ENV UWSGI_PROCESSES 1 -RUN pip install --no-cache-dir git+https://github.com/eclipse-basyx/basyx-python-sdk@main +RUN pip install --no-cache-dir git+https://github.com/eclipse-basyx/basyx-python-sdk@main#subdirectory=sdk COPY ./app /app COPY ./nginx /etc/nginx/conf.d From 7cfe4de5761885db83375f5a5954f797e09d7447 Mon Sep 17 00:00:00 2001 From: Frosty2500 <125310380+Frosty2500@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:40:10 +0100 Subject: [PATCH 21/25] Refactor towards a Dockerfile from Python-Alpine (#11) Previously our Dockerfile started from the `tiangolo/uwsgi-nginx:python3.11` image. While there's nothing wrong with using a very specialized image that is built for running Python-based servers with nginx, it does limit us in what we can modify. Therefore, we decided to refactor our Dockerfile to start from Python-Alpine and install the required tools manually. --- server/Dockerfile | 45 ++++++++++++++++--- server/README.md | 3 ++ server/compose.yml | 4 +- server/entrypoint.sh | 71 ++++++++++++++++++++++++++++++ server/nginx/body-buffer-size.conf | 6 --- server/nginx/cors-header.conf | 1 - server/stop-supervisor.sh | 8 ++++ server/supervisord.ini | 27 ++++++++++++ server/uwsgi.ini | 9 ++++ 9 files changed, 158 insertions(+), 16 deletions(-) create mode 100644 server/entrypoint.sh delete mode 100644 server/nginx/body-buffer-size.conf delete mode 100644 server/nginx/cors-header.conf create mode 100644 server/stop-supervisor.sh create mode 100644 server/supervisord.ini create mode 100644 server/uwsgi.ini diff --git a/server/Dockerfile b/server/Dockerfile index 92542120..6dc3c4ca 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,14 +1,45 @@ -FROM tiangolo/uwsgi-nginx:python3.11 +FROM python:3.11-alpine -# Should match client_body_buffer_size defined in nginx/body-buffer-size.conf -ENV NGINX_MAX_UPLOAD 1M +LABEL org.label-schema.name="Eclipse BaSyx" \ + org.label-schema.version="1.0" \ + org.label-schema.description="Docker image for the basyx-python-sdk server application" \ + org.label-schema.maintainer="Eclipse BaSyx" +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +# If we have more dependencies for the server it would make sense +# to refactor uswgi to the pyproject.toml +RUN apk update && \ + apk add --no-cache nginx supervisor gcc musl-dev linux-headers python3-dev git bash && \ + pip install uwsgi && \ + pip install --no-cache-dir git+https://github.com/eclipse-basyx/basyx-python-sdk@main#subdirectory=sdk && \ + apk del git bash + + +COPY uwsgi.ini /etc/uwsgi/ +COPY supervisord.ini /etc/supervisor/conf.d/supervisord.ini +COPY stop-supervisor.sh /etc/supervisor/stop-supervisor.sh +RUN chmod +x /etc/supervisor/stop-supervisor.sh + +# Makes it possible to use a different configuration +ENV UWSGI_INI=/etc/uwsgi/uwsgi.ini # object stores aren't thread-safe yet # https://github.com/eclipse-basyx/basyx-python-sdk/issues/205 -ENV UWSGI_CHEAPER 0 -ENV UWSGI_PROCESSES 1 +ENV UWSGI_CHEAPER=0 +ENV UWSGI_PROCESSES=1 +ENV NGINX_MAX_UPLOAD=1M +ENV NGINX_WORKER_PROCESSES=1 +ENV LISTEN_PORT=80 +ENV CLIENT_BODY_BUFFER_SIZE=1M + +# Copy the entrypoint that will generate Nginx additional configs +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh -RUN pip install --no-cache-dir git+https://github.com/eclipse-basyx/basyx-python-sdk@main#subdirectory=sdk +ENTRYPOINT ["/entrypoint.sh"] COPY ./app /app -COPY ./nginx /etc/nginx/conf.d +WORKDIR /app + +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.ini"] diff --git a/server/README.md b/server/README.md index 853f45ef..27330b73 100644 --- a/server/README.md +++ b/server/README.md @@ -58,7 +58,9 @@ Per default, the server will use the `LOCAL_FILE_READ_ONLY` storage type and ser ``` $ docker run -p 8080:80 -v ./storage2:/storage2 -e API_BASE_PATH=/api/v3.1 -e STORAGE_TYPE=LOCAL_FILE_BACKEND -e STORAGE_PATH=/storage2 basyx-python-sdk-http-server ``` +## Acknowledgments +This Dockerfile is inspired by the [tiangolo/uwsgi-nginx-docker][10] repository. [1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 [2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html @@ -69,3 +71,4 @@ $ docker run -p 8080:80 -v ./storage2:/storage2 -e API_BASE_PATH=/api/v3.1 -e ST [7]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/aasx.html#adapter-aasx [8]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/json.html [9]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/xml.html +[10]: https://github.com/tiangolo/uwsgi-nginx-docker diff --git a/server/compose.yml b/server/compose.yml index 263a9519..5465e04c 100644 --- a/server/compose.yml +++ b/server/compose.yml @@ -2,6 +2,6 @@ services: app: build: . ports: - - 8080:80 + - "8080:80" volumes: - - ./storage:/app/storage + - ./storage:/storage diff --git a/server/entrypoint.sh b/server/entrypoint.sh new file mode 100644 index 00000000..72239440 --- /dev/null +++ b/server/entrypoint.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env sh +set -e + +# Get the maximum upload file size for Nginx, default to 0: unlimited +USE_NGINX_MAX_UPLOAD=${NGINX_MAX_UPLOAD:-0} + +# Get the number of workers for Nginx, default to 1 +USE_NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-1} + +# Set the max number of connections per worker for Nginx, if requested +# Cannot exceed worker_rlimit_nofile, see NGINX_WORKER_OPEN_FILES below +NGINX_WORKER_CONNECTIONS=${NGINX_WORKER_CONNECTIONS:-1024} + +# Get the listen port for Nginx, default to 80 +USE_LISTEN_PORT=${LISTEN_PORT:-80} + +# Get the client_body_buffer_size for Nginx, default to 1M +USE_CLIENT_BODY_BUFFER_SIZE=${CLIENT_BODY_BUFFER_SIZE:-1M} + +# Create the conf.d directory if it doesn't exist +if [ ! -d /etc/nginx/conf.d ]; then + mkdir -p /etc/nginx/conf.d +fi + +if [ -f /app/nginx.conf ]; then + cp /app/nginx.conf /etc/nginx/nginx.conf +else + content='user nginx;\n' + # Set the number of worker processes in Nginx + content=$content"worker_processes ${USE_NGINX_WORKER_PROCESSES};\n" + content=$content'error_log /var/log/nginx/error.log warn;\n' + content=$content'pid /var/run/nginx.pid;\n' + content=$content'events {\n' + content=$content" worker_connections ${NGINX_WORKER_CONNECTIONS};\n" + content=$content'}\n' + content=$content'http {\n' + content=$content' include /etc/nginx/mime.types;\n' + content=$content' default_type application/octet-stream;\n' + content=$content' log_format main '"'\$remote_addr - \$remote_user [\$time_local] \"\$request\" '\n" + content=$content' '"'\$status \$body_bytes_sent \"\$http_referer\" '\n" + content=$content' '"'\"\$http_user_agent\" \"\$http_x_forwarded_for\"';\n" + content=$content' access_log /var/log/nginx/access.log main;\n' + content=$content' sendfile on;\n' + content=$content' keepalive_timeout 65;\n' + content=$content' include /etc/nginx/conf.d/*.conf;\n' + content=$content'}\n' + content=$content'daemon off;\n' + # Set the max number of open file descriptors for Nginx workers, if requested + if [ -n "${NGINX_WORKER_OPEN_FILES}" ] ; then + content=$content"worker_rlimit_nofile ${NGINX_WORKER_OPEN_FILES};\n" + fi + # Save generated /etc/nginx/nginx.conf + printf "$content" > /etc/nginx/nginx.conf + + content_server='server {\n' + content_server=$content_server" listen ${USE_LISTEN_PORT};\n" + content_server=$content_server' location / {\n' + content_server=$content_server' include uwsgi_params;\n' + content_server=$content_server' uwsgi_pass unix:///tmp/uwsgi.sock;\n' + content_server=$content_server' }\n' + content_server=$content_server'}\n' + # Save generated server /etc/nginx/conf.d/nginx.conf + printf "$content_server" > /etc/nginx/conf.d/nginx.conf + + # # Generate additional configuration + printf "client_max_body_size $USE_NGINX_MAX_UPLOAD;\n" > /etc/nginx/conf.d/upload.conf + printf "client_body_buffer_size $USE_CLIENT_BODY_BUFFER_SIZE;\n" > /etc/nginx/conf.d/body-buffer-size.conf + printf "add_header Access-Control-Allow-Origin *;\n" > /etc/nginx/conf.d/cors-header.conf +fi + +exec "$@" diff --git a/server/nginx/body-buffer-size.conf b/server/nginx/body-buffer-size.conf deleted file mode 100644 index 423e8cd6..00000000 --- a/server/nginx/body-buffer-size.conf +++ /dev/null @@ -1,6 +0,0 @@ -# While the Dockerfile sets client_max_body_size, it doesn't -# allow us to define client_body_buffer_size, which is 16k -# by default. This results in temporary buffer files being -# created, in case the request body exceeds this limit. Thus, -# we override it here to match client_max_body_size. -client_body_buffer_size 1M; diff --git a/server/nginx/cors-header.conf b/server/nginx/cors-header.conf deleted file mode 100644 index af78db3a..00000000 --- a/server/nginx/cors-header.conf +++ /dev/null @@ -1 +0,0 @@ -add_header Access-Control-Allow-Origin *; diff --git a/server/stop-supervisor.sh b/server/stop-supervisor.sh new file mode 100644 index 00000000..9a953c94 --- /dev/null +++ b/server/stop-supervisor.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +printf "READY\n" + +while read line; do + echo "Processing Event: $line" >&2 + kill $PPID +done < /dev/stdin diff --git a/server/supervisord.ini b/server/supervisord.ini new file mode 100644 index 00000000..d73d9801 --- /dev/null +++ b/server/supervisord.ini @@ -0,0 +1,27 @@ +[supervisord] +nodaemon=true + +[program:uwsgi] +command=/usr/local/bin/uwsgi --ini /etc/uwsgi/uwsgi.ini +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +startsecs = 0 +autorestart=false +# may make sense to have autorestart enabled in production + +[program:nginx] +command=/usr/sbin/nginx +stdout_logfile=/var/log/nginx.out.log +stdout_logfile_maxbytes=0 +stderr_logfile=/var/log/nginx.err.log +stderr_logfile_maxbytes=0 +stopsignal=QUIT +startsecs = 0 +autorestart=false +# may make sense to have autorestart enabled in production + +[eventlistener:quit_on_failure] +events=PROCESS_STATE_STOPPED,PROCESS_STATE_EXITED,PROCESS_STATE_FATAL +command=/etc/supervisor/stop-supervisor.sh diff --git a/server/uwsgi.ini b/server/uwsgi.ini new file mode 100644 index 00000000..9c54ae1c --- /dev/null +++ b/server/uwsgi.ini @@ -0,0 +1,9 @@ +[uwsgi] +wsgi-file = /app/main.py +socket = /tmp/uwsgi.sock +chown-socket = nginx:nginx +chmod-socket = 664 +hook-master-start = unix_signal:15 gracefully_kill_them_all +need-app = true +die-on-term = true +show-config = false From e39a39e97f6fda99f7bce34aea973e8e5c25549f Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Fri, 15 Nov 2024 15:01:58 +0100 Subject: [PATCH 22/25] implement requested changes --- .github/workflows/ci.yml | 44 ++++++++++++++++++++++++++++++++++++++++ .gitignore | 3 +++ server/README.md | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ccd9944..05f62c1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,3 +146,47 @@ jobs: - name: Create source and wheel dist run: | python -m build + + + server-static-analysis: + # This job runs static code analysis, namely pycodestyle and mypy + # TODO: check shell scripts and Dockerfile with tools like ShellCheck and hadolint + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./server + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ env.X_PYTHON_VERSION }} + uses: actions/setup-python@v2 + with: + python-version: ${{ env.X_PYTHON_VERSION }} + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install .[dev] + - name: Check typing with MyPy + run: | + mypy app test + - name: Check code style with PyCodestyle + run: | + pycodestyle --count --max-line-length 120 app test + server-package: + # This job checks if we can build our server package + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./server + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ env.X_PYTHON_VERSION }} + uses: actions/setup-python@v2 + with: + python-version: ${{ env.X_PYTHON_VERSION }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Create source and wheel dist + run: | + python -m build \ No newline at end of file diff --git a/.gitignore b/.gitignore index 463a0f0f..dc7eddbb 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ sdk/test/adapter/schemas # Ignore dynamically generated version file sdk/basyx/version.py + +# ignore the content of the server storage +server/storage/ diff --git a/server/README.md b/server/README.md index 27330b73..a2df4818 100644 --- a/server/README.md +++ b/server/README.md @@ -1,6 +1,6 @@ # Eclipse BaSyx Python SDK - HTTP Server -This repository contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API][6] with ease. +This package contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API][6] with ease. The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] From 947d3bad3c43eafaca148878ce187e62273d337f Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Fri, 15 Nov 2024 15:06:19 +0100 Subject: [PATCH 23/25] implement requested changes --- server/.gitignore | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 server/.gitignore diff --git a/server/.gitignore b/server/.gitignore deleted file mode 100644 index af40b724..00000000 --- a/server/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Ignore the default AAS storage directory -storage/ - -# Ignore PyCharm IDE configuration -.idea From 317ac0bb3409f814ec3ca9b5d8eb6abad83da587 Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Fri, 15 Nov 2024 15:14:49 +0100 Subject: [PATCH 24/25] remove unfinished ci changes --- .github/workflows/ci.yml | 44 ---------------------------------------- 1 file changed, 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05f62c1c..9ccd9944 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,47 +146,3 @@ jobs: - name: Create source and wheel dist run: | python -m build - - - server-static-analysis: - # This job runs static code analysis, namely pycodestyle and mypy - # TODO: check shell scripts and Dockerfile with tools like ShellCheck and hadolint - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./server - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ env.X_PYTHON_VERSION }} - uses: actions/setup-python@v2 - with: - python-version: ${{ env.X_PYTHON_VERSION }} - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install .[dev] - - name: Check typing with MyPy - run: | - mypy app test - - name: Check code style with PyCodestyle - run: | - pycodestyle --count --max-line-length 120 app test - server-package: - # This job checks if we can build our server package - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./server - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ env.X_PYTHON_VERSION }} - uses: actions/setup-python@v2 - with: - python-version: ${{ env.X_PYTHON_VERSION }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Create source and wheel dist - run: | - python -m build \ No newline at end of file From 898c1ddc2363f9ab634cae8a8a3c4758195e55ae Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Sat, 16 Nov 2024 17:04:50 +0100 Subject: [PATCH 25/25] add docker-compose documentation --- server/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/server/README.md b/server/README.md index a2df4818..23b0b817 100644 --- a/server/README.md +++ b/server/README.md @@ -58,6 +58,34 @@ Per default, the server will use the `LOCAL_FILE_READ_ONLY` storage type and ser ``` $ docker run -p 8080:80 -v ./storage2:/storage2 -e API_BASE_PATH=/api/v3.1 -e STORAGE_TYPE=LOCAL_FILE_BACKEND -e STORAGE_PATH=/storage2 basyx-python-sdk-http-server ``` + +## Building and running the image with docker-compose + +The container image can also be built via: +``` +$ docker-compose build +``` + +And then run using: +``` +$ docker-compose up +``` + +This is the exemplary `docker-compose` file of this repository: +````yaml +services: + app: + build: . + ports: + - "8080:80" + volumes: + - ./storage:/storage + +```` + +Here files are read from `/storage` and the server can be accessed at http://localhost:8080/api/v3.0/ from your host system. +To get a different setup this compose.yaml file can be adapted and expanded. + ## Acknowledgments This Dockerfile is inspired by the [tiangolo/uwsgi-nginx-docker][10] repository.