From 45e7b288aa89aa79b2c7ba38c20840bd3e4a56dc Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 30 May 2023 17:19:41 +0100 Subject: [PATCH 1/3] Initial Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- template/python3-fastapi-debian/Dockerfile | 55 +++++++++++++ .../function/handler.py | 7 ++ .../function/handler_test.py | 10 +++ .../function/requirements.txt | 0 template/python3-fastapi-debian/main.py | 80 +++++++++++++++++++ .../python3-fastapi-debian/requirements.txt | 2 + template/python3-fastapi-debian/template.yml | 7 ++ 7 files changed, 161 insertions(+) create mode 100644 template/python3-fastapi-debian/Dockerfile create mode 100644 template/python3-fastapi-debian/function/handler.py create mode 100644 template/python3-fastapi-debian/function/handler_test.py create mode 100644 template/python3-fastapi-debian/function/requirements.txt create mode 100644 template/python3-fastapi-debian/main.py create mode 100644 template/python3-fastapi-debian/requirements.txt create mode 100644 template/python3-fastapi-debian/template.yml diff --git a/template/python3-fastapi-debian/Dockerfile b/template/python3-fastapi-debian/Dockerfile new file mode 100644 index 0000000..928cdfb --- /dev/null +++ b/template/python3-fastapi-debian/Dockerfile @@ -0,0 +1,55 @@ +ARG PYTHON_VERSION=3.11 +FROM --platform=${TARGETPLATFORM:-linux/amd64} ghcr.io/openfaas/of-watchdog:0.9.11 as watchdog +FROM --platform=${TARGETPLATFORM:-linux/amd64} python:${PYTHON_VERSION}-slim-buster as build + +COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog +RUN chmod +x /usr/bin/fwatchdog + +ARG ADDITIONAL_PACKAGE +# Alternatively use ADD https:// (which will not be cached by Docker builder) + +RUN apt-get -qy update \ + && apt-get -qy install curl ${ADDITIONAL_PACKAGE} \ + && rm -rf /var/lib/apt/lists/* + +# Add non root user +RUN addgroup --system app && adduser app --system --ingroup app +RUN chown app /home/app + +USER app + +ENV PATH=$PATH:/home/app/.local/bin + +WORKDIR /home/app/ + +COPY --chown=app:app main.py . +COPY --chown=app:app requirements.txt . + +USER root +RUN pip install --no-cache-dir -r requirements.txt +USER app + +RUN mkdir -p function +RUN touch ./function/__init__.py +WORKDIR /home/app/function/ +COPY --chown=app:app function/requirements.txt . +RUN pip install --no-cache-dir --user -r requirements.txt + +USER root +COPY --chown=app:app function/ . + +FROM build as ship +WORKDIR /home/app/ + +USER app + +ENV fprocess="uvicorn main:app" +ENV cgi_headers="true" +ENV mode="http" +ENV upstream_url="http://127.0.0.1:5000" + +ENV UVICORN_PORT=5000 + +HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 + +CMD ["fwatchdog"] diff --git a/template/python3-fastapi-debian/function/handler.py b/template/python3-fastapi-debian/function/handler.py new file mode 100644 index 0000000..315e91f --- /dev/null +++ b/template/python3-fastapi-debian/function/handler.py @@ -0,0 +1,7 @@ +def handle(event, context): + + + return { + "statusCode": 200, + "body": "Hello from OpenFaaS! {}".format(event.body.decode("utf-8")), + } diff --git a/template/python3-fastapi-debian/function/handler_test.py b/template/python3-fastapi-debian/function/handler_test.py new file mode 100644 index 0000000..b07d5bf --- /dev/null +++ b/template/python3-fastapi-debian/function/handler_test.py @@ -0,0 +1,10 @@ +from .handler import handle + +# Test your handler here + +# To disable testing, you can set the build_arg `TEST_ENABLED=false` on the CLI or in your stack.yml +# https://docs.openfaas.com/reference/yaml/#function-build-args-build-args + +def test_handle(): + # assert handle("input") == "input" + pass diff --git a/template/python3-fastapi-debian/function/requirements.txt b/template/python3-fastapi-debian/function/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/template/python3-fastapi-debian/main.py b/template/python3-fastapi-debian/main.py new file mode 100644 index 0000000..75f44e6 --- /dev/null +++ b/template/python3-fastapi-debian/main.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +from fastapi import FastAPI, Request + +app = FastAPI() + +import os + +from function import handler + +class Event: + def __init__(self, request: Request,body ): + self.body = body + self.headers = request.headers + self.method = request.method + self.query = request.query_params + self.path = request.url.path + +class Context: + def __init__(self): + self.hostname = os.getenv('HOSTNAME', 'localhost') + +def format_status_code(res): + if 'statusCode' in res: + return res['statusCode'] + + return 200 + +def format_body(res, content_type): + if content_type == 'application/octet-stream': + return res['body'] + + if 'body' not in res: + return "" + elif type(res['body']) == dict: + return jsonify(res['body']) + else: + return str(res['body']) + +def format_headers(res): + if 'headers' not in res: + return [] + elif type(res['headers']) == dict: + headers = [] + for key in res['headers'].keys(): + header_tuple = (key, res['headers'][key]) + headers.append(header_tuple) + return headers + + return res['headers'] + +def get_content_type(res): + content_type = "" + if 'headers' in res: + content_type = res['headers'].get('Content-type', '') + return content_type + +def format_response(res): + if res == None: + return ('', 200) + + statusCode = format_status_code(res) + content_type = get_content_type(res) + body = format_body(res, content_type) + + headers = format_headers(res) + + return (body, statusCode, headers) + +@app.api_route("/", methods=["GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD"]) +async def root(request: Request): + + body = await request.body() + event = Event(request, body) + + context = Context() + + response_data = handler.handle(event, context) + + res = format_response(response_data) + return res diff --git a/template/python3-fastapi-debian/requirements.txt b/template/python3-fastapi-debian/requirements.txt new file mode 100644 index 0000000..d1f80a3 --- /dev/null +++ b/template/python3-fastapi-debian/requirements.txt @@ -0,0 +1,2 @@ +uvicorn +fastapi \ No newline at end of file diff --git a/template/python3-fastapi-debian/template.yml b/template/python3-fastapi-debian/template.yml new file mode 100644 index 0000000..390cc4b --- /dev/null +++ b/template/python3-fastapi-debian/template.yml @@ -0,0 +1,7 @@ +language: python3-fastapi-debian +build_options: + - name: libpq + packages: + - libpq-dev + - gcc + - python3-dev From 1496336ec43bec2e5d4e848b047ebfe9790cbf0f Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 30 May 2023 17:33:13 +0100 Subject: [PATCH 2/3] Add sample function to generate 10mb of response size Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- .gitignore | 3 +++ stack.yml | 10 ++++++++ template/python3-fastapi-debian/main.py | 3 ++- template/python3-fastapi-debian/template.yml | 1 + variable-payload/handler.py | 27 ++++++++++++++++++++ variable-payload/handler_test.py | 10 ++++++++ variable-payload/r.py | 20 +++++++++++++++ variable-payload/requirements.txt | 0 8 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 stack.yml create mode 100644 variable-payload/handler.py create mode 100644 variable-payload/handler_test.py create mode 100644 variable-payload/r.py create mode 100644 variable-payload/requirements.txt diff --git a/.gitignore b/.gitignore index 378eac2..9938efb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ build + +template +.secrets diff --git a/stack.yml b/stack.yml new file mode 100644 index 0000000..9e04246 --- /dev/null +++ b/stack.yml @@ -0,0 +1,10 @@ +version: 1.0 +provider: + name: openfaas + gateway: http://127.0.0.1:8080 +functions: + variable-payload: + lang: python3-fastapi-debian + handler: ./variable-payload + image: ttl.sh/variable-payload:latest + diff --git a/template/python3-fastapi-debian/main.py b/template/python3-fastapi-debian/main.py index 75f44e6..760f817 100644 --- a/template/python3-fastapi-debian/main.py +++ b/template/python3-fastapi-debian/main.py @@ -1,5 +1,6 @@ #!/usr/bin/env python from fastapi import FastAPI, Request +import json app = FastAPI() @@ -32,7 +33,7 @@ def format_body(res, content_type): if 'body' not in res: return "" elif type(res['body']) == dict: - return jsonify(res['body']) + return json.dumps(res['body']) else: return str(res['body']) diff --git a/template/python3-fastapi-debian/template.yml b/template/python3-fastapi-debian/template.yml index 390cc4b..7dfdbca 100644 --- a/template/python3-fastapi-debian/template.yml +++ b/template/python3-fastapi-debian/template.yml @@ -5,3 +5,4 @@ build_options: - libpq-dev - gcc - python3-dev +fprocess: uvicorn main:app diff --git a/variable-payload/handler.py b/variable-payload/handler.py new file mode 100644 index 0000000..a80b41c --- /dev/null +++ b/variable-payload/handler.py @@ -0,0 +1,27 @@ +import random +import sys + +def handle(event, context): + payload_size_mb = 1 + + if "payload_size_mb" in event.query: + payload_size_mb = int(event.query["payload_size_mb"]) + + sys.stderr.write("payload_size_mb: {}\n".format(payload_size_mb)) + + ret_val = "" + # Generate a random number for ASCII between A and Z + for i in range(0, 1024*1024): + random_ch = random.randint(65, 90) + ret_val += chr(random_ch) + + # Generate a random string of size: payload_size_mb + ret = { + "payload_size_mb": payload_size_mb, + "payload": ret_val, + } + + return { + "statusCode": 200, + "body": ret, + } diff --git a/variable-payload/handler_test.py b/variable-payload/handler_test.py new file mode 100644 index 0000000..b07d5bf --- /dev/null +++ b/variable-payload/handler_test.py @@ -0,0 +1,10 @@ +from .handler import handle + +# Test your handler here + +# To disable testing, you can set the build_arg `TEST_ENABLED=false` on the CLI or in your stack.yml +# https://docs.openfaas.com/reference/yaml/#function-build-args-build-args + +def test_handle(): + # assert handle("input") == "input" + pass diff --git a/variable-payload/r.py b/variable-payload/r.py new file mode 100644 index 0000000..26ad147 --- /dev/null +++ b/variable-payload/r.py @@ -0,0 +1,20 @@ +import random + +def h(): + payload_size_mb = 1 + + ret_val = "" + # Generate a random number for ASCII between A and Z + for i in range(0, 1024*1024): + random_ch = random.randint(65, 90) + ret_val += chr(random_ch) + + # Generate a random string of size: payload_size_mb + ret = { + "payload_size_mb": payload_size_mb, + "payload": ret_val, + } + + return ret + +print(h()) \ No newline at end of file diff --git a/variable-payload/requirements.txt b/variable-payload/requirements.txt new file mode 100644 index 0000000..e69de29 From d67d0bfe72b07ffe6ccdca18b456b3ea39ff592d Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 30 May 2023 17:36:17 +0100 Subject: [PATCH 3/3] Add variable to increase payload size Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- variable-payload/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable-payload/handler.py b/variable-payload/handler.py index a80b41c..1a9910c 100644 --- a/variable-payload/handler.py +++ b/variable-payload/handler.py @@ -11,7 +11,7 @@ def handle(event, context): ret_val = "" # Generate a random number for ASCII between A and Z - for i in range(0, 1024*1024): + for i in range(0, 1024*1024*payload_size_mb): random_ch = random.randint(65, 90) ret_val += chr(random_ch)