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/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..760f817 --- /dev/null +++ b/template/python3-fastapi-debian/main.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +from fastapi import FastAPI, Request +import json + +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 json.dumps(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..7dfdbca --- /dev/null +++ b/template/python3-fastapi-debian/template.yml @@ -0,0 +1,8 @@ +language: python3-fastapi-debian +build_options: + - name: libpq + packages: + - 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..1a9910c --- /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*payload_size_mb): + 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