├── .DEREK.yml ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── only-ci.yaml ├── .gitignore ├── LICENSE ├── README.md ├── template ├── python27-flask │ ├── Dockerfile │ ├── function │ │ ├── __init__.py │ │ ├── handler.py │ │ └── requirements.txt │ ├── index.py │ ├── requirements.txt │ └── template.yml ├── python3-flask-debian │ ├── Dockerfile │ ├── function │ │ ├── __init__.py │ │ ├── handler.py │ │ ├── handler_test.py │ │ ├── requirements.txt │ │ └── tox.ini │ ├── index.py │ ├── requirements.txt │ └── template.yml ├── python3-flask │ ├── Dockerfile │ ├── function │ │ ├── __init__.py │ │ ├── handler.py │ │ ├── handler_test.py │ │ ├── requirements.txt │ │ └── tox.ini │ ├── index.py │ ├── requirements.txt │ └── template.yml ├── python3-http-debian │ ├── Dockerfile │ ├── function │ │ ├── handler.py │ │ ├── handler_test.py │ │ ├── requirements.txt │ │ └── tox.ini │ ├── index.py │ ├── requirements.txt │ └── template.yml └── python3-http │ ├── Dockerfile │ ├── function │ ├── handler.py │ ├── handler_test.py │ ├── requirements.txt │ └── tox.ini │ ├── index.py │ ├── requirements.txt │ └── template.yml └── verify.sh /.DEREK.yml: -------------------------------------------------------------------------------- 1 | redirect: https://raw.githubusercontent.com/openfaas/faas/master/.DEREK.yml 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## My actions before raising this issue 4 | - [ ] Followed the [troubleshooting guide](https://docs.openfaas.com/deployment/troubleshooting/) 5 | - [ ] Read/searched [the docs](https://docs.openfaas.com/) 6 | - [ ] Searched [past issues](/issues) 7 | 8 | 9 | 10 | 11 | ## Expected Behaviour 12 | 13 | 14 | 15 | 16 | ## Current Behaviour 17 | 18 | 19 | 20 | 21 | ## Possible Solution 22 | 23 | 24 | 25 | 26 | ## Steps to Reproduce (for bugs) 27 | 28 | 29 | 1. 30 | 2. 31 | 3. 32 | 4. 33 | 34 | ## Context 35 | 36 | 37 | 38 | 39 | ## Your Environment 40 | 41 | * FaaS-CLI version ( Full output from: `faas-cli version` ): 42 | 43 | * Docker version `docker version` (e.g. Docker 17.0.05 ): 44 | 45 | * Are you using Docker Swarm or Kubernetes (FaaS-netes)? 46 | 47 | * Operating System and version (e.g. Linux, Windows, MacOS): 48 | 49 | * Code example or link to GitHub repo or gist to reproduce problem: 50 | 51 | * Other diagnostic information / logs from [troubleshooting guide](https://docs.openfaas.com/deployment/troubleshooting) 52 | 53 | ## Next steps 54 | 55 | You may [join Slack](https://docs.openfaas.com/community) for community support. 56 | 57 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | - [ ] I have raised an issue to propose this change ([required](https://github.com/openfaas/faas/blob/master/CONTRIBUTING.md)) 10 | - [ ] My issue has received approval from the maintainers or lead with the `design/approved` label 11 | 12 | 13 | ## How Has This Been Tested? 14 | 15 | 16 | 17 | 18 | 19 | ## Types of changes 20 | 21 | - [ ] Bug fix (non-breaking change which fixes an issue) 22 | - [ ] New feature (non-breaking change which adds functionality) 23 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 24 | 25 | 26 | ## Checklist: 27 | 28 | 29 | - [ ] My code follows the code style of this project. 30 | - [ ] My change requires a change to the documentation. 31 | - [ ] I have updated the documentation accordingly. 32 | - [ ] I've read the [CONTRIBUTION](https://github.com/openfaas/faas/blob/master/CONTRIBUTING.md) guide 33 | - [ ] I have signed-off my commits with `git commit -s` 34 | - [ ] I have added tests to cover my changes. 35 | - [ ] All new and existing tests passed. 36 | -------------------------------------------------------------------------------- /.github/workflows/only-ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci-only 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: 9 | - opened 10 | - synchronize 11 | - reopened 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 1 20 | - name: Setup faas-cli 21 | run: curl -sSL https://cli.openfaas.com | sh 22 | - name: Verify all templates 23 | run: bash verify.sh 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alex Ellis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OpenFaaS Python Flask Templates 2 | ============================================= 3 | 4 | The Python Flask templates that make use of the incubator project [of-watchdog](https://github.com/openfaas-incubator/of-watchdog). 5 | 6 | Templates available in this repository: 7 | 8 | - python3-http 9 | - python3-http-debian (ideal for compiled dependencies like numpy, pandas, pillow) 10 | 11 | - python3-flask 12 | - python3-flask-debian (ideal for compiled dependencies like numpy, pandas, pillow) 13 | 14 | - python27-flask (Python 2.7 is deprecated) 15 | 16 | Notes: 17 | - To build and deploy a function for an ARM computer, you'll need to use `faas-cli publish --platforms` 18 | 19 | ## SSH authentication for private Git repositories and Pip modules 20 | 21 | If you need to install Pip modules from private Git repositories, we provide an alternative set of templates for OpenFaaS Pro customers: 22 | 23 | * [OpenFaaS Pro templates for Python](https://github.com/openfaas/pro-templates) 24 | 25 | ## Picking your template 26 | 27 | The templates named `python*-flask*` are designed as a drop-in replacement for the classic `python3` template, but using the more efficient of-watchdog. The move to use flask as an underlying framework allows for greater control over the HTTP request and response. 28 | 29 | Those templates named `python*-http*` are designed to offer full control over the HTTP request and response. Flask is used as an underlying framework. 30 | 31 | The `witness` HTTP server is used along with Flask for all templates. 32 | 33 | Are you referencing pip modules which require a native build toolchain? It's advisable to use the template with a `-debian` suffix in this case. The Debian images are larger, however they are usually more efficient for use with modules like `numpy` and `pandas`. 34 | 35 | ## Python Versioning 36 | 37 | We try to keep the default Python 3 version up-to-date, however, you can specify a specific python version using the `PYTHON_VERSION` build argument. 38 | 39 | The current stable version of Python is 3.12, you might want to test the next pre-release using: 40 | 41 | ```yaml 42 | functions: 43 | pgfn: 44 | lang: python3-http-debian 45 | handler: ./pgfn 46 | image: pgfn:latest 47 | build_args: 48 | - PYTHON_VERSION=3.12 49 | ``` 50 | Or pin to a older version while you wait for your dependencies to be updated. 51 | 52 | ```yaml 53 | functions: 54 | pgfn: 55 | lang: python3-http-debian 56 | handler: ./pgfn 57 | image: pgfn:latest 58 | build_args: 59 | - PYTHON_VERSION=3.10 60 | ``` 61 | 62 | This can also be set using the `--build-arg` flag. 63 | 64 | ```bash 65 | faas-cli build --build-arg PYTHON_VERSION=3.12 66 | ``` 67 | 68 | For the `-debian` templates, the `DEBIAN_OS` build_args is also available to specify the Debian version. The default is currently `bookworm`. 69 | 70 | ## Downloading the templates 71 | 72 | Using template pull with the repository's URL: 73 | 74 | ```bash 75 | faas-cli template pull https://github.com/openfaas-incubator/python-flask-template 76 | ``` 77 | 78 | Using the template store: 79 | 80 | ```bash 81 | # Either command downloads both templates 82 | faas-cli template store pull python3-http 83 | 84 | # Or 85 | faas-cli template store pull python3-flask 86 | ``` 87 | 88 | Using your `stack.yml` file: 89 | 90 | ```yaml 91 | configuration: 92 | templates: 93 | - name: python3-http 94 | ``` 95 | 96 | # Using the python3-http templates 97 | 98 | Create a new function 99 | 100 | ``` 101 | export OPENFAAS_PREFIX=alexellis2 102 | export FN="tester" 103 | faas-cli new --lang python3-http $FN 104 | ``` 105 | 106 | Build, push, and deploy 107 | 108 | ``` 109 | faas-cli up -f $FN.yml 110 | ``` 111 | 112 | Test the new function 113 | 114 | ``` 115 | echo -n content | faas-cli invoke $FN 116 | ``` 117 | 118 | ## Event and Context Data 119 | 120 | The function handler is passed two arguments, *event* and *context*. 121 | 122 | *event* contains data about the request, including: 123 | - body 124 | - headers 125 | - method 126 | - query 127 | - path 128 | 129 | *context* contains basic information about the function, including: 130 | - hostname 131 | 132 | ## Response Bodies 133 | 134 | By default, the template will automatically attempt to set the correct Content-Type header for you based on the type of response. 135 | 136 | For example, returning a dict object type will automatically attach the header `Content-Type: application/json` and returning a string type will automatically attach the `Content-Type: text/html, charset=utf-8` for you. 137 | 138 | ## Example usage 139 | 140 | ### Return a JSON body with a Content-Type 141 | 142 | ```python 143 | def handle(event, context): 144 | return { 145 | "statusCode": 200, 146 | "body": {"message": "Hello from OpenFaaS!"}, 147 | "headers": { 148 | "Content-Type": "application/json" 149 | } 150 | } 151 | ``` 152 | 153 | ### Custom status codes and response bodies 154 | 155 | Successful response status code and JSON response body 156 | ```python 157 | def handle(event, context): 158 | return { 159 | "statusCode": 200, 160 | "body": { 161 | "key": "value" 162 | } 163 | } 164 | ``` 165 | Successful response status code and string response body 166 | ```python 167 | def handle(event, context): 168 | return { 169 | "statusCode": 201, 170 | "body": "Object successfully created" 171 | } 172 | ``` 173 | Failure response status code and JSON error message 174 | ```python 175 | def handle(event, context): 176 | return { 177 | "statusCode": 400, 178 | "body": { 179 | "error": "Bad request" 180 | } 181 | } 182 | ``` 183 | ### Custom Response Headers 184 | Setting custom response headers 185 | ```python 186 | def handle(event, context): 187 | return { 188 | "statusCode": 200, 189 | "body": { 190 | "key": "value" 191 | }, 192 | "headers": { 193 | "Location": "https://www.example.com/" 194 | } 195 | } 196 | ``` 197 | ### Accessing Event Data 198 | Accessing request body 199 | ```python 200 | def handle(event, context): 201 | return { 202 | "statusCode": 200, 203 | "body": "You said: " + str(event.body) 204 | } 205 | ``` 206 | Accessing request method 207 | ```python 208 | def handle(event, context): 209 | if event.method == 'GET': 210 | return { 211 | "statusCode": 200, 212 | "body": "GET request" 213 | } 214 | else: 215 | return { 216 | "statusCode": 405, 217 | "body": "Method not allowed" 218 | } 219 | ``` 220 | Accessing request query string arguments 221 | ```python 222 | def handle(event, context): 223 | return { 224 | "statusCode": 200, 225 | "body": { 226 | "name": event.query['name'] 227 | } 228 | } 229 | ``` 230 | Accessing request headers 231 | ```python 232 | def handle(event, context): 233 | return { 234 | "statusCode": 200, 235 | "body": { 236 | "content-type-received": event.headers.get('Content-Type') 237 | } 238 | } 239 | ``` 240 | 241 | ### Example with Postgresql: 242 | 243 | stack.yml 244 | 245 | ```yaml 246 | version: 1.0 247 | provider: 248 | name: openfaas 249 | gateway: http://127.0.0.1:8080 250 | functions: 251 | pgfn: 252 | lang: python3-http-debian 253 | handler: ./pgfn 254 | image: pgfn:latest 255 | build_options: 256 | - libpq 257 | ``` 258 | 259 | Alternatively you can specify `ADDITIONAL_PACKAGE` in the `build_args` section for the function. 260 | 261 | ```yaml 262 | build_args: 263 | ADDITIONAL_PACKAGE: "libpq-dev gcc python3-dev" 264 | ``` 265 | 266 | requirements.txt 267 | 268 | ``` 269 | psycopg2==2.9.3 270 | ``` 271 | 272 | Create a database and table: 273 | 274 | ```sql 275 | CREATE DATABASE main; 276 | 277 | \c main; 278 | 279 | CREATE TABLE users ( 280 | name TEXT, 281 | ); 282 | 283 | -- Insert the original Postgresql author's name into the test table: 284 | 285 | INSERT INTO users (name) VALUES ('Michael Stonebraker'); 286 | ``` 287 | 288 | handler.py: 289 | 290 | ```python 291 | import psycopg2 292 | 293 | def handle(event, context): 294 | 295 | try: 296 | conn = psycopg2.connect("dbname='main' user='postgres' port=5432 host='192.168.1.35' password='passwd'") 297 | except Exception as e: 298 | print("DB error {}".format(e)) 299 | return { 300 | "statusCode": 500, 301 | "body": e 302 | } 303 | 304 | cur = conn.cursor() 305 | cur.execute("""SELECT * from users;""") 306 | rows = cur.fetchall() 307 | 308 | return { 309 | "statusCode": 200, 310 | "body": rows 311 | } 312 | ``` 313 | 314 | Always read the secret from an OpenFaaS secret at `/var/openfaas/secrets/secret-name`. The use of environment variables is an anti-pattern and will be visible via the OpenFaaS API. 315 | 316 | # Using the python3-flask template 317 | 318 | Create a new function 319 | 320 | ``` 321 | export OPENFAAS_PREFIX=alexellis2 322 | export FN="tester" 323 | faas-cli new --lang python3-flask $FN 324 | ``` 325 | 326 | Build, push, and deploy 327 | 328 | ``` 329 | faas-cli up -f $FN.yml 330 | ``` 331 | 332 | Test the new function 333 | 334 | ``` 335 | echo -n content | faas-cli invoke $FN 336 | ``` 337 | 338 | ## Example of returning a string 339 | 340 | ```python 341 | def handle(req): 342 | """handle a request to the function 343 | Args: 344 | req (str): request body 345 | """ 346 | 347 | return "Hi" + str(req) 348 | ``` 349 | 350 | ## Example of returning a custom HTTP code 351 | 352 | ```python 353 | def handle(req): 354 | return "request accepted", 201 355 | ``` 356 | 357 | ## Example of returning a custom HTTP code and content-type 358 | 359 | ```python 360 | def handle(req): 361 | return "request accepted", 201, {"Content-Type":"binary/octet-stream"} 362 | ``` 363 | 364 | ## Example of accepting raw bytes in the request 365 | 366 | Update stack.yml: 367 | 368 | ```yaml 369 | environment: 370 | RAW_BODY: True 371 | ``` 372 | 373 | > Note: the value for `RAW_BODY` is case-sensitive. 374 | 375 | ```python 376 | def handle(req): 377 | """handle a request to the function 378 | Args: 379 | req (str): request body 380 | """ 381 | 382 | # req is bytes, so an input of "hello" returns i.e. b'hello' 383 | return str(req) 384 | ``` 385 | 386 | 387 | ## Testing 388 | The `python3` templates will run `pytest` using `tox` during the `faas-cli build`. There are several options for controlling this. 389 | 390 | ### Disabling testing 391 | The template exposes the build arg `TEST_ENABLED`. You can completely disable testing during build by passing the following flag to the CLI 392 | 393 | ```sh 394 | --build-arg 'TEST_ENABLED=false' 395 | ``` 396 | 397 | You can also set it permanently in your stack.yaml, see the [YAML reference in the docs](https://docs.openfaas.com/reference/yaml/#function-build-args-build-args). 398 | 399 | ### Changing the test configuration 400 | The template creates a default `tox.ini` file, modifying this file can completely control what happens during the test. You can change the test command, for example switching to `nose`. See the [tox docs](https://tox.readthedocs.io/en/latest/index.html) for more details and examples. 401 | 402 | ### Changing the test command 403 | If you don't want to use `tox` at all, you can also change the test command that is used. The template exposes the build arg `TEST_COMMAND`. You can override the test command during build by passing the following flag to the CLI 404 | 405 | ```sh 406 | --build-arg 'TEST_COMMAND=bash test.sh' 407 | ``` 408 | Setting the command to any other executable in the image or any scripts you have in your function. 409 | 410 | You can also set it permanently in your stack.yaml, see the [YAML reference in the docs](https://docs.openfaas.com/reference/yaml/#function-build-args-build-args). 411 | -------------------------------------------------------------------------------- /template/python27-flask/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/openfaas/of-watchdog:0.10.7 AS watchdog 2 | FROM python:2.7-alpine AS build 3 | 4 | COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog 5 | RUN chmod +x /usr/bin/fwatchdog 6 | 7 | ARG ADDITIONAL_PACKAGE 8 | # Alternatively use ADD https:// (which will not be cached by Docker builder) 9 | RUN apk --no-cache add ${ADDITIONAL_PACKAGE} 10 | 11 | # Add non root user 12 | RUN addgroup -S app && adduser app -S -G app 13 | RUN chown app /home/app 14 | 15 | USER app 16 | 17 | ENV PATH=$PATH:/home/app/.local/bin 18 | 19 | WORKDIR /home/app/ 20 | 21 | COPY --chown=app:app index.py . 22 | COPY --chown=app:app requirements.txt . 23 | USER root 24 | RUN pip install --no-cache-dir -r requirements.txt 25 | USER app 26 | 27 | RUN mkdir -p function 28 | RUN touch ./function/__init__.py 29 | WORKDIR /home/app/function/ 30 | COPY --chown=app:app function/requirements.txt . 31 | RUN pip install --no-cache-dir --user -r requirements.txt 32 | 33 | WORKDIR /home/app/ 34 | 35 | USER root 36 | COPY --chown=app:app function function 37 | USER app 38 | 39 | FROM build AS ship 40 | 41 | ENV fprocess="python index.py" 42 | ENV cgi_headers="true" 43 | ENV mode="http" 44 | ENV upstream_url="http://127.0.0.1:5000" 45 | 46 | 47 | HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 48 | 49 | CMD ["fwatchdog"] 50 | -------------------------------------------------------------------------------- /template/python27-flask/function/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python27-flask/function/__init__.py -------------------------------------------------------------------------------- /template/python27-flask/function/handler.py: -------------------------------------------------------------------------------- 1 | def handle(req): 2 | """handle a request to the function 3 | Args: 4 | req (str): request body 5 | """ 6 | 7 | return req 8 | -------------------------------------------------------------------------------- /template/python27-flask/function/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python27-flask/function/requirements.txt -------------------------------------------------------------------------------- /template/python27-flask/index.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Alex Ellis 2017. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | from flask import Flask, request 5 | from function import handler 6 | 7 | app = Flask(__name__) 8 | 9 | @app.before_request 10 | def fix_transfer_encoding(): 11 | """ 12 | Sets the "wsgi.input_terminated" environment flag, thus enabling 13 | Werkzeug to pass chunked requests as streams. The gunicorn server 14 | should set this, but it's not yet been implemented. 15 | """ 16 | 17 | transfer_encoding = request.headers.get("Transfer-Encoding", None) 18 | if transfer_encoding == u"chunked": 19 | request.environ["wsgi.input_terminated"] = True 20 | 21 | @app.route("/", defaults={"path": ""}, methods=["POST", "GET"]) 22 | @app.route("/", methods=["POST", "GET"]) 23 | def main_route(path): 24 | ret = handler.handle(request.get_data()) 25 | return ret 26 | 27 | if __name__ == '__main__': 28 | app.run(host='0.0.0.0', port=5000, debug=False) 29 | -------------------------------------------------------------------------------- /template/python27-flask/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | 3 | -------------------------------------------------------------------------------- /template/python27-flask/template.yml: -------------------------------------------------------------------------------- 1 | language: python27-flask 2 | fprocess: python index.py 3 | -------------------------------------------------------------------------------- /template/python3-flask-debian/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PYTHON_VERSION=3.12 2 | ARG DEBIAN_OS=slim-bookworm 3 | ARG UPGRADE_PACKAGES=false 4 | 5 | FROM --platform=${TARGETPLATFORM:-linux/amd64} ghcr.io/openfaas/of-watchdog:0.10.7 AS watchdog 6 | FROM --platform=${TARGETPLATFORM:-linux/amd64} python:${PYTHON_VERSION}-${DEBIAN_OS} AS build 7 | 8 | COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog 9 | RUN chmod +x /usr/bin/fwatchdog 10 | 11 | ARG ADDITIONAL_PACKAGE 12 | # Alternatively use ADD https:// (which will not be cached by Docker builder) 13 | 14 | RUN apt-get update -qy \ 15 | && if [ "${UPGRADE_PACKAGES}" = "true" ] || [ "${UPGRADE_PACKAGES}" = "1" ]; then apt-get upgrade -qy; fi \ 16 | && apt-get install -qy --no-install-recommends gcc make ${ADDITIONAL_PACKAGE} \ 17 | && rm -rf /var/lib/apt/lists/* 18 | 19 | # Add non root user 20 | RUN addgroup --system app \ 21 | && adduser app --system --ingroup app --home /home/app \ 22 | && chown app:app /home/app 23 | 24 | USER app 25 | 26 | ENV PATH=$PATH:/home/app/.local/bin 27 | 28 | WORKDIR /home/app/ 29 | 30 | COPY --chown=app:app index.py . 31 | COPY --chown=app:app requirements.txt . 32 | 33 | USER root 34 | RUN pip install --no-cache-dir -r requirements.txt 35 | 36 | # Build the function directory and install any user-specified components 37 | USER app 38 | 39 | RUN mkdir -p function 40 | RUN touch ./function/__init__.py 41 | WORKDIR /home/app/function/ 42 | COPY --chown=app:app function/requirements.txt . 43 | RUN pip install --no-cache-dir --user -r requirements.txt 44 | 45 | #install function code 46 | USER root 47 | 48 | COPY --chown=app:app function/ . 49 | 50 | FROM build AS test 51 | ARG TEST_COMMAND=tox 52 | ARG TEST_ENABLED=true 53 | RUN [ "$TEST_ENABLED" = "false" ] && echo "skipping tests" || eval "$TEST_COMMAND" 54 | 55 | 56 | FROM build AS ship 57 | WORKDIR /home/app/ 58 | 59 | #configure WSGI server and healthcheck 60 | USER app 61 | 62 | ENV fprocess="python index.py" 63 | 64 | ENV cgi_headers="true" 65 | ENV mode="http" 66 | ENV upstream_url="http://127.0.0.1:5000" 67 | 68 | HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 69 | 70 | CMD ["fwatchdog"] 71 | -------------------------------------------------------------------------------- /template/python3-flask-debian/function/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python3-flask-debian/function/__init__.py -------------------------------------------------------------------------------- /template/python3-flask-debian/function/handler.py: -------------------------------------------------------------------------------- 1 | def handle(req): 2 | """handle a request to the function 3 | Args: 4 | req (str): request body 5 | """ 6 | 7 | return req 8 | -------------------------------------------------------------------------------- /template/python3-flask-debian/function/handler_test.py: -------------------------------------------------------------------------------- 1 | from .handler import handle 2 | 3 | # Test your handler here 4 | 5 | # To disable testing, you can set the build_arg `TEST_ENABLED=false` on the CLI or in your stack.yml 6 | # https://docs.openfaas.com/reference/yaml/#function-build-args-build-args 7 | 8 | def test_handle(): 9 | # assert handle("input") == "input" 10 | pass 11 | -------------------------------------------------------------------------------- /template/python3-flask-debian/function/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python3-flask-debian/function/requirements.txt -------------------------------------------------------------------------------- /template/python3-flask-debian/function/tox.ini: -------------------------------------------------------------------------------- 1 | # If you would like to disable 2 | # automated testing during faas-cli build, 3 | 4 | # Replace the content of this file with 5 | # [tox] 6 | # skipsdist = true 7 | 8 | # You can also edit, remove, or add additional test steps 9 | # by editing, removing, or adding new testenv sections 10 | 11 | 12 | # find out more about tox: https://tox.readthedocs.io/en/latest/ 13 | [tox] 14 | envlist = lint,test 15 | skipsdist = true 16 | 17 | [testenv:test] 18 | deps = 19 | flask 20 | pytest 21 | -rrequirements.txt 22 | commands = 23 | # run unit tests with pytest 24 | # https://docs.pytest.org/en/stable/ 25 | # configure by adding a pytest.ini to your handler 26 | pytest 27 | 28 | [testenv:lint] 29 | deps = 30 | flake8 31 | commands = 32 | flake8 . 33 | 34 | [flake8] 35 | count = true 36 | max-line-length = 127 37 | max-complexity = 10 38 | statistics = true 39 | # stop the build if there are Python syntax errors or undefined names 40 | select = E9,F63,F7,F82 41 | show-source = true 42 | -------------------------------------------------------------------------------- /template/python3-flask-debian/index.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Alex Ellis 2017. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | from flask import Flask, request 5 | from function import handler 6 | from waitress import serve 7 | import os 8 | 9 | app = Flask(__name__) 10 | 11 | # distutils.util.strtobool() can throw an exception 12 | def is_true(val): 13 | return len(val) > 0 and val.lower() == "true" or val == "1" 14 | 15 | @app.before_request 16 | def fix_transfer_encoding(): 17 | """ 18 | Sets the "wsgi.input_terminated" environment flag, thus enabling 19 | Werkzeug to pass chunked requests as streams. The gunicorn server 20 | should set this, but it's not yet been implemented. 21 | """ 22 | 23 | transfer_encoding = request.headers.get("Transfer-Encoding", None) 24 | if transfer_encoding == u"chunked": 25 | request.environ["wsgi.input_terminated"] = True 26 | 27 | @app.route("/", defaults={"path": ""}, methods=["POST", "GET"]) 28 | @app.route("/", methods=["POST", "GET"]) 29 | def main_route(path): 30 | raw_body = os.getenv("RAW_BODY", "false") 31 | 32 | as_text = True 33 | 34 | if is_true(raw_body): 35 | as_text = False 36 | 37 | ret = handler.handle(request.get_data(as_text=as_text)) 38 | return ret 39 | 40 | if __name__ == '__main__': 41 | serve(app, host='0.0.0.0', port=5000) 42 | -------------------------------------------------------------------------------- /template/python3-flask-debian/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | waitress 3 | tox==3.* 4 | -------------------------------------------------------------------------------- /template/python3-flask-debian/template.yml: -------------------------------------------------------------------------------- 1 | language: python3-flask-debian 2 | fprocess: python index.py 3 | -------------------------------------------------------------------------------- /template/python3-flask/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PYTHON_VERSION=3.12 2 | FROM --platform=${TARGETPLATFORM:-linux/amd64} ghcr.io/openfaas/of-watchdog:0.10.7 AS watchdog 3 | FROM --platform=${TARGETPLATFORM:-linux/amd64} python:${PYTHON_VERSION}-alpine AS build 4 | 5 | COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog 6 | RUN chmod +x /usr/bin/fwatchdog 7 | 8 | ARG UPGRADE_PACKAGES=false 9 | 10 | ARG ADDITIONAL_PACKAGE 11 | # Alternatively use ADD https:// (which will not be cached by Docker builder) 12 | 13 | RUN if [ "${UPGRADE_PACKAGES}" = "true" ] || [ "${UPGRADE_PACKAGES}" = "1" ]; then apk --no-cache upgrade; fi && \ 14 | apk --no-cache add openssl-dev ${ADDITIONAL_PACKAGE} 15 | 16 | # Add non root user 17 | RUN addgroup -S app && adduser app -S -G app 18 | RUN chown app /home/app 19 | 20 | USER app 21 | 22 | ENV PATH=$PATH:/home/app/.local/bin 23 | 24 | WORKDIR /home/app/ 25 | 26 | COPY --chown=app:app index.py . 27 | COPY --chown=app:app requirements.txt . 28 | 29 | USER root 30 | RUN pip install --no-cache-dir -r requirements.txt 31 | 32 | # Build the function directory and install any user-specified components 33 | USER app 34 | 35 | RUN mkdir -p function 36 | RUN touch ./function/__init__.py 37 | WORKDIR /home/app/function/ 38 | COPY --chown=app:app function/requirements.txt . 39 | RUN pip install --no-cache-dir --user -r requirements.txt 40 | 41 | #install function code 42 | USER root 43 | 44 | COPY --chown=app:app function/ . 45 | 46 | 47 | FROM build AS test 48 | ARG TEST_COMMAND=tox 49 | ARG TEST_ENABLED=true 50 | RUN [ "$TEST_ENABLED" = "false" ] && echo "skipping tests" || eval "$TEST_COMMAND" 51 | 52 | FROM build AS ship 53 | WORKDIR /home/app/ 54 | 55 | #configure WSGI server and healthcheck 56 | USER app 57 | 58 | ENV fprocess="python index.py" 59 | 60 | ENV cgi_headers="true" 61 | ENV mode="http" 62 | ENV upstream_url="http://127.0.0.1:5000" 63 | 64 | HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 65 | 66 | CMD ["fwatchdog"] 67 | -------------------------------------------------------------------------------- /template/python3-flask/function/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python3-flask/function/__init__.py -------------------------------------------------------------------------------- /template/python3-flask/function/handler.py: -------------------------------------------------------------------------------- 1 | def handle(req): 2 | """handle a request to the function 3 | Args: 4 | req (str): request body 5 | """ 6 | 7 | return req 8 | -------------------------------------------------------------------------------- /template/python3-flask/function/handler_test.py: -------------------------------------------------------------------------------- 1 | from .handler import handle 2 | 3 | # Test your handler here 4 | 5 | # To disable testing, you can set the build_arg `TEST_ENABLED=false` on the CLI or in your stack.yml 6 | # https://docs.openfaas.com/reference/yaml/#function-build-args-build-args 7 | 8 | def test_handle(): 9 | # assert handle("input") == "input" 10 | pass 11 | -------------------------------------------------------------------------------- /template/python3-flask/function/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python3-flask/function/requirements.txt -------------------------------------------------------------------------------- /template/python3-flask/function/tox.ini: -------------------------------------------------------------------------------- 1 | # If you would like to disable 2 | # automated testing during faas-cli build, 3 | 4 | # Replace the content of this file with 5 | # [tox] 6 | # skipsdist = true 7 | 8 | # You can also edit, remove, or add additional test steps 9 | # by editing, removing, or adding new testenv sections 10 | 11 | 12 | # find out more about tox: https://tox.readthedocs.io/en/latest/ 13 | [tox] 14 | envlist = lint,test 15 | skipsdist = true 16 | 17 | [testenv:test] 18 | deps = 19 | flask 20 | pytest 21 | -rrequirements.txt 22 | commands = 23 | # run unit tests with pytest 24 | # https://docs.pytest.org/en/stable/ 25 | # configure by adding a pytest.ini to your handler 26 | pytest 27 | 28 | [testenv:lint] 29 | deps = 30 | flake8 31 | commands = 32 | flake8 . 33 | 34 | [flake8] 35 | count = true 36 | max-line-length = 127 37 | max-complexity = 10 38 | statistics = true 39 | # stop the build if there are Python syntax errors or undefined names 40 | select = E9,F63,F7,F82 41 | show-source = true 42 | -------------------------------------------------------------------------------- /template/python3-flask/index.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Alex Ellis 2017. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | from flask import Flask, request 5 | from function import handler 6 | from waitress import serve 7 | import os 8 | 9 | app = Flask(__name__) 10 | 11 | # distutils.util.strtobool() can throw an exception 12 | def is_true(val): 13 | return len(val) > 0 and val.lower() == "true" or val == "1" 14 | 15 | @app.before_request 16 | def fix_transfer_encoding(): 17 | """ 18 | Sets the "wsgi.input_terminated" environment flag, thus enabling 19 | Werkzeug to pass chunked requests as streams. The gunicorn server 20 | should set this, but it's not yet been implemented. 21 | """ 22 | 23 | transfer_encoding = request.headers.get("Transfer-Encoding", None) 24 | if transfer_encoding == u"chunked": 25 | request.environ["wsgi.input_terminated"] = True 26 | 27 | @app.route("/", defaults={"path": ""}, methods=["POST", "GET"]) 28 | @app.route("/", methods=["POST", "GET"]) 29 | def main_route(path): 30 | raw_body = os.getenv("RAW_BODY", "false") 31 | 32 | as_text = True 33 | 34 | if is_true(raw_body): 35 | as_text = False 36 | 37 | ret = handler.handle(request.get_data(as_text=as_text)) 38 | return ret 39 | 40 | if __name__ == '__main__': 41 | serve(app, host='0.0.0.0', port=5000) 42 | -------------------------------------------------------------------------------- /template/python3-flask/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | waitress 3 | tox==3.* -------------------------------------------------------------------------------- /template/python3-flask/template.yml: -------------------------------------------------------------------------------- 1 | language: python3-flask 2 | fprocess: python index.py 3 | -------------------------------------------------------------------------------- /template/python3-http-debian/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PYTHON_VERSION=3.12 2 | ARG DEBIAN_OS=slim-bookworm 3 | 4 | 5 | FROM --platform=${TARGETPLATFORM:-linux/amd64} ghcr.io/openfaas/of-watchdog:0.10.7 AS watchdog 6 | FROM --platform=${TARGETPLATFORM:-linux/amd64} python:${PYTHON_VERSION}-${DEBIAN_OS} AS build 7 | 8 | COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog 9 | RUN chmod +x /usr/bin/fwatchdog 10 | 11 | ARG ADDITIONAL_PACKAGE 12 | # Alternatively use ADD https:// (which will not be cached by Docker builder) 13 | ARG UPGRADE_PACKAGES=false 14 | 15 | RUN apt-get update -qy \ 16 | && if [ "${UPGRADE_PACKAGES}" = "true" ] || [ "${UPGRADE_PACKAGES}" = "1" ]; then apt-get upgrade -qy; fi \ 17 | && apt-get install -qy --no-install-recommends gcc make ${ADDITIONAL_PACKAGE} \ 18 | && rm -rf /var/lib/apt/lists/* 19 | 20 | # Add non root user 21 | RUN addgroup --system app \ 22 | && adduser app --system --ingroup app --home /home/app \ 23 | && chown app:app /home/app 24 | 25 | USER app 26 | 27 | ENV PATH=$PATH:/home/app/.local/bin 28 | 29 | WORKDIR /home/app/ 30 | 31 | COPY --chown=app:app index.py . 32 | COPY --chown=app:app requirements.txt . 33 | USER root 34 | RUN pip install --no-cache-dir -r requirements.txt 35 | USER app 36 | 37 | RUN mkdir -p function 38 | RUN touch ./function/__init__.py 39 | WORKDIR /home/app/function/ 40 | COPY --chown=app:app function/requirements.txt . 41 | RUN pip install --no-cache-dir --user -r requirements.txt 42 | 43 | USER root 44 | COPY --chown=app:app function/ . 45 | 46 | FROM build AS test 47 | 48 | ARG TEST_COMMAND=tox 49 | ARG TEST_ENABLED=true 50 | RUN [ "$TEST_ENABLED" = "false" ] && echo "skipping tests" || eval "$TEST_COMMAND" 51 | 52 | 53 | FROM build AS ship 54 | WORKDIR /home/app/ 55 | 56 | USER app 57 | 58 | # Set up of-watchdog for HTTP mode 59 | ENV fprocess="python index.py" 60 | ENV cgi_headers="true" 61 | ENV mode="http" 62 | ENV upstream_url="http://127.0.0.1:5000" 63 | 64 | HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 65 | 66 | CMD ["fwatchdog"] 67 | -------------------------------------------------------------------------------- /template/python3-http-debian/function/handler.py: -------------------------------------------------------------------------------- 1 | def handle(event, context): 2 | return { 3 | "statusCode": 200, 4 | "body": "Hello from OpenFaaS!" 5 | } 6 | -------------------------------------------------------------------------------- /template/python3-http-debian/function/handler_test.py: -------------------------------------------------------------------------------- 1 | from .handler import handle 2 | 3 | # Test your handler here 4 | 5 | # To disable testing, you can set the build_arg `TEST_ENABLED=false` on the CLI or in your stack.yml 6 | # https://docs.openfaas.com/reference/yaml/#function-build-args-build-args 7 | 8 | def test_handle(): 9 | # assert handle("input") == "input" 10 | pass 11 | -------------------------------------------------------------------------------- /template/python3-http-debian/function/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python3-http-debian/function/requirements.txt -------------------------------------------------------------------------------- /template/python3-http-debian/function/tox.ini: -------------------------------------------------------------------------------- 1 | # If you would like to disable 2 | # automated testing during faas-cli build, 3 | 4 | # Replace the content of this file with 5 | # [tox] 6 | # skipsdist = true 7 | 8 | # You can also edit, remove, or add additional test steps 9 | # by editing, removing, or adding new testenv sections 10 | 11 | 12 | # find out more about tox: https://tox.readthedocs.io/en/latest/ 13 | [tox] 14 | envlist = lint,test 15 | skipsdist = true 16 | 17 | [testenv:test] 18 | deps = 19 | flask 20 | pytest 21 | -rrequirements.txt 22 | commands = 23 | # run unit tests with pytest 24 | # https://docs.pytest.org/en/stable/ 25 | # configure by adding a pytest.ini to your handler 26 | pytest 27 | 28 | [testenv:lint] 29 | deps = 30 | flake8 31 | commands = 32 | flake8 . 33 | 34 | [flake8] 35 | count = true 36 | max-line-length = 127 37 | max-complexity = 10 38 | statistics = true 39 | # stop the build if there are Python syntax errors or undefined names 40 | select = E9,F63,F7,F82 41 | show-source = true 42 | -------------------------------------------------------------------------------- /template/python3-http-debian/index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from flask import Flask, request, jsonify 3 | from waitress import serve 4 | import os 5 | 6 | from function import handler 7 | 8 | app = Flask(__name__) 9 | 10 | class Event: 11 | def __init__(self): 12 | self.body = request.get_data() 13 | self.headers = request.headers 14 | self.method = request.method 15 | self.query = request.args 16 | self.path = request.path 17 | 18 | class Context: 19 | def __init__(self): 20 | self.hostname = os.getenv('HOSTNAME', 'localhost') 21 | 22 | def format_status_code(res): 23 | if 'statusCode' in res: 24 | return res['statusCode'] 25 | 26 | return 200 27 | 28 | def format_body(res, content_type): 29 | if content_type == 'application/octet-stream': 30 | return res['body'] 31 | 32 | if 'body' not in res: 33 | return "" 34 | elif type(res['body']) == dict: 35 | return jsonify(res['body']) 36 | else: 37 | return str(res['body']) 38 | 39 | def format_headers(res): 40 | if 'headers' not in res: 41 | return [] 42 | elif type(res['headers']) == dict: 43 | headers = [] 44 | for key in res['headers'].keys(): 45 | header_tuple = (key, res['headers'][key]) 46 | headers.append(header_tuple) 47 | return headers 48 | 49 | return res['headers'] 50 | 51 | def get_content_type(res): 52 | content_type = "" 53 | if 'headers' in res: 54 | content_type = res['headers'].get('Content-type', '') 55 | return content_type 56 | 57 | def format_response(res): 58 | if res == None: 59 | return ('', 200) 60 | 61 | if type(res) is dict: 62 | statusCode = format_status_code(res) 63 | content_type = get_content_type(res) 64 | body = format_body(res, content_type) 65 | 66 | headers = format_headers(res) 67 | 68 | return (body, statusCode, headers) 69 | 70 | return res 71 | 72 | @app.route('/', defaults={'path': ''}, methods=['GET', 'PUT', 'POST', 'PATCH', 'DELETE']) 73 | @app.route('/', methods=['GET', 'PUT', 'POST', 'PATCH', 'DELETE']) 74 | def call_handler(path): 75 | event = Event() 76 | context = Context() 77 | 78 | response_data = handler.handle(event, context) 79 | 80 | res = format_response(response_data) 81 | return res 82 | 83 | if __name__ == '__main__': 84 | serve(app, host='0.0.0.0', port=5000) 85 | -------------------------------------------------------------------------------- /template/python3-http-debian/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | waitress 3 | tox==3.* -------------------------------------------------------------------------------- /template/python3-http-debian/template.yml: -------------------------------------------------------------------------------- 1 | language: python3-http-debian 2 | fprocess: python index.py 3 | build_options: 4 | - name: libpq 5 | packages: 6 | - libpq-dev 7 | - gcc 8 | - python3-dev 9 | -------------------------------------------------------------------------------- /template/python3-http/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PYTHON_VERSION=3.12 2 | FROM --platform=${TARGETPLATFORM:-linux/amd64} ghcr.io/openfaas/of-watchdog:0.10.7 AS watchdog 3 | FROM --platform=${TARGETPLATFORM:-linux/amd64} python:${PYTHON_VERSION}-alpine AS build 4 | 5 | COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog 6 | RUN chmod +x /usr/bin/fwatchdog 7 | 8 | ARG UPGRADE_PACKAGES 9 | ARG ADDITIONAL_PACKAGE 10 | # Alternatively use ADD https:// (which will not be cached by Docker builder) 11 | 12 | RUN if [ "${UPGRADE_PACKAGES}" = "true" ] || [ "${UPGRADE_PACKAGES}" = "1" ]; then apk --no-cache upgrade; fi && \ 13 | apk --no-cache add ${ADDITIONAL_PACKAGE} 14 | 15 | # Add non root user 16 | RUN addgroup -S app && adduser app -S -G app 17 | RUN chown app /home/app 18 | 19 | USER app 20 | 21 | ENV PATH=$PATH:/home/app/.local/bin 22 | 23 | WORKDIR /home/app/ 24 | 25 | COPY --chown=app:app index.py . 26 | COPY --chown=app:app requirements.txt . 27 | USER root 28 | RUN pip install --no-cache-dir -r requirements.txt 29 | 30 | # Build the function directory and install any user-specified components 31 | USER app 32 | 33 | RUN mkdir -p function 34 | RUN touch ./function/__init__.py 35 | WORKDIR /home/app/function/ 36 | COPY --chown=app:app function/requirements.txt . 37 | RUN pip install --no-cache-dir --user -r requirements.txt 38 | 39 | # install function code 40 | USER root 41 | COPY --chown=app:app function/ . 42 | 43 | FROM build AS test 44 | ARG TEST_COMMAND=tox 45 | ARG TEST_ENABLED=true 46 | RUN [ "$TEST_ENABLED" = "false" ] && echo "skipping tests" || eval "$TEST_COMMAND" 47 | 48 | FROM build AS ship 49 | WORKDIR /home/app/ 50 | 51 | # configure WSGI server and healthcheck 52 | USER app 53 | 54 | ENV fprocess="python index.py" 55 | ENV cgi_headers="true" 56 | ENV mode="http" 57 | ENV upstream_url="http://127.0.0.1:5000" 58 | 59 | HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 60 | 61 | CMD ["fwatchdog"] 62 | -------------------------------------------------------------------------------- /template/python3-http/function/handler.py: -------------------------------------------------------------------------------- 1 | def handle(event, context): 2 | return { 3 | "statusCode": 200, 4 | "body": "Hello from OpenFaaS!" 5 | } 6 | -------------------------------------------------------------------------------- /template/python3-http/function/handler_test.py: -------------------------------------------------------------------------------- 1 | from .handler import handle 2 | 3 | # Test your handler here 4 | 5 | # To disable testing, you can set the build_arg `TEST_ENABLED=false` on the CLI or in your stack.yml 6 | # https://docs.openfaas.com/reference/yaml/#function-build-args-build-args 7 | 8 | def test_handle(): 9 | # assert handle("input") == "input" 10 | pass 11 | -------------------------------------------------------------------------------- /template/python3-http/function/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfaas/python-flask-template/40d0094dbd6a650fa38aa27a43e0b55100551a67/template/python3-http/function/requirements.txt -------------------------------------------------------------------------------- /template/python3-http/function/tox.ini: -------------------------------------------------------------------------------- 1 | # If you would like to disable 2 | # automated testing during faas-cli build, 3 | 4 | # Replace the content of this file with 5 | # [tox] 6 | # skipsdist = true 7 | 8 | # You can also edit, remove, or add additional test steps 9 | # by editing, removing, or adding new testenv sections 10 | 11 | 12 | # find out more about tox: https://tox.readthedocs.io/en/latest/ 13 | [tox] 14 | envlist = lint,test 15 | skipsdist = true 16 | 17 | [testenv:test] 18 | deps = 19 | flask 20 | pytest 21 | -rrequirements.txt 22 | commands = 23 | # run unit tests with pytest 24 | # https://docs.pytest.org/en/stable/ 25 | # configure by adding a pytest.ini to your handler 26 | pytest 27 | 28 | [testenv:lint] 29 | deps = 30 | flake8 31 | commands = 32 | flake8 . 33 | 34 | [flake8] 35 | count = true 36 | max-line-length = 127 37 | max-complexity = 10 38 | statistics = true 39 | # stop the build if there are Python syntax errors or undefined names 40 | select = E9,F63,F7,F82 41 | show-source = true 42 | -------------------------------------------------------------------------------- /template/python3-http/index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from flask import Flask, request, jsonify 3 | from waitress import serve 4 | import os 5 | 6 | from function import handler 7 | 8 | app = Flask(__name__) 9 | 10 | class Event: 11 | def __init__(self): 12 | self.body = request.get_data() 13 | self.headers = request.headers 14 | self.method = request.method 15 | self.query = request.args 16 | self.path = request.path 17 | 18 | class Context: 19 | def __init__(self): 20 | self.hostname = os.getenv('HOSTNAME', 'localhost') 21 | 22 | def format_status_code(resp): 23 | if 'statusCode' in resp: 24 | return resp['statusCode'] 25 | 26 | return 200 27 | 28 | def format_body(resp): 29 | if 'body' not in resp: 30 | return "" 31 | elif type(resp['body']) == dict: 32 | return jsonify(resp['body']) 33 | else: 34 | return str(resp['body']) 35 | 36 | def format_headers(resp): 37 | if 'headers' not in resp: 38 | return [] 39 | elif type(resp['headers']) == dict: 40 | headers = [] 41 | for key in resp['headers'].keys(): 42 | header_tuple = (key, resp['headers'][key]) 43 | headers.append(header_tuple) 44 | return headers 45 | 46 | return resp['headers'] 47 | 48 | def format_response(resp): 49 | if resp == None: 50 | return ('', 200) 51 | 52 | if type(resp) is dict: 53 | statusCode = format_status_code(resp) 54 | body = format_body(resp) 55 | headers = format_headers(resp) 56 | 57 | return (body, statusCode, headers) 58 | 59 | return resp 60 | 61 | @app.route('/', defaults={'path': ''}, methods=['GET', 'PUT', 'POST', 'PATCH', 'DELETE']) 62 | @app.route('/', methods=['GET', 'PUT', 'POST', 'PATCH', 'DELETE']) 63 | def call_handler(path): 64 | event = Event() 65 | context = Context() 66 | response_data = handler.handle(event, context) 67 | 68 | resp = format_response(response_data) 69 | return resp 70 | 71 | if __name__ == '__main__': 72 | serve(app, host='0.0.0.0', port=5000) 73 | -------------------------------------------------------------------------------- /template/python3-http/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | waitress 3 | tox==3.* -------------------------------------------------------------------------------- /template/python3-http/template.yml: -------------------------------------------------------------------------------- 1 | language: python3-http 2 | fprocess: python index.py 3 | build_options: 4 | - name: dev 5 | packages: 6 | - make 7 | - automake 8 | - gcc 9 | - g++ 10 | - subversion 11 | - python3-dev 12 | - musl-dev 13 | - libffi-dev 14 | - git -------------------------------------------------------------------------------- /verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | CLI="faas-cli" 5 | 6 | build_template() { 7 | template=$1 8 | 9 | echo Building $template 10 | func_name=$template-ci 11 | $CLI new $func_name --lang $template 2>/dev/null 1>&2 12 | $CLI build -f $func_name.yml 13 | } 14 | 15 | verify_and_clean() { 16 | image=$1 17 | tag_name=latest 18 | 19 | echo Verifying $template 20 | container=$(docker run -d -p 8080:8080 $func_name:$tag_name) 21 | sleep 5 # wait for slower templates to start 22 | output=$(curl -s -d "testing" http://127.0.0.1:8080) 23 | 24 | echo $image output: $output 25 | success=false 26 | if [ ! -z "$output" ]; then # output was not empty = good template 27 | success=true 28 | fi 29 | 30 | echo Cleaning $image 31 | docker rm $container -f 2>/dev/null 1>&2 32 | docker rmi $func_name:$tag_name 2>/dev/null 1>&2 33 | 34 | if [ "$success" = false ]; then 35 | echo $image template failed validation 36 | exit 1 37 | else 38 | echo $image template validation successful 39 | fi 40 | } 41 | 42 | if ! [ -x "$(command -v faas-cli)" ]; then 43 | HERE=$(pwd) 44 | cd /tmp/ 45 | curl -sSL https://cli.openfaas.com | sh 46 | CLI="/tmp/faas-cli" 47 | 48 | cd $HERE 49 | fi 50 | 51 | cli_version=$($CLI version --short-version) 52 | 53 | echo Validating templates with faas-cli $cli_version 54 | 55 | cd ./template 56 | 57 | # verify each of the templates 58 | for dir in ./*/; do 59 | dirname=${dir%*/} 60 | template=${dirname##*/} 61 | 62 | # skip arm templates 63 | case "$template" in 64 | *-arm*) continue ;; 65 | esac 66 | 67 | pushd ../ 2>/dev/null 1>&2 68 | 69 | build_template $template 70 | verify_and_clean $template 71 | 72 | popd 2>/dev/null 1>&2 73 | done 74 | 75 | # remove the generated files and folders if successful 76 | cd ../ 77 | rm -rf *-ci *-ci.yml 78 | --------------------------------------------------------------------------------