Skip to content

Commit 461c2be

Browse files
kturciosalexellis
authored andcommitted
Update the handler to match style of golang-http-template
- Created two new templates, python3-http & python3-http-armhf - Added waitress as the production server for new templates Signed-off-by: Kevin Turcios <[email protected]>
1 parent 070bba5 commit 461c2be

File tree

14 files changed

+421
-20
lines changed

14 files changed

+421
-20
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
*.pyc
21
build

README.md

+145-19
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,158 @@
1-
# python-flask-template
1+
OpenFaaS Python Flask Templates
2+
=============================================
23

3-
Python OpenFaaS template with Flask
4+
The Python Flask templates make use of the incubator project [of-watchdog](https://github.com/openfaas-incubator/of-watchdog) to handle higher throughput than the [classic watchdog](https://github.com/openfaas/faas/tree/master/watchdog) due to the process being kept warm.
45

5-
To try this out with either Python 2.7 or Python 3.6:
6-
7-
```bash
8-
faas template pull https://github.com/openfaas-incubator/python-flask-template
9-
faas new --list
10-
Languages available as templates:
6+
Templates available under this repository:
117
- python27-flask
128
- python3-flask
13-
```
9+
- python3-flask-armhf
10+
- python3-http*
11+
- python3-http-armhf*
1412

15-
Generate a function with one of the languages:
13+
Notes:
14+
- The templates listed with an asterik are the only ones that support additional control over the HTTP response and provide access to HTTP request details.
15+
- To build and deploy a function for Raspberry Pi or ARMv7 in general, use the language templates ending in *-armhf*
1616

17-
```bash
18-
faas new --lang python3-flask myfunction
19-
mv myfunction.yml stack.yml
17+
## Downloading the templates
18+
```
19+
$ faas template pull https://github.com/openfaas-incubator/python-flask-template
2020
```
2121

22-
Followed by the usual flow:
22+
# Using the python27-flask/python3-flask templates
23+
Create a new function
24+
```
25+
$ faas new --lang python27-flask <fn-name>
26+
```
27+
Build, push, and deploy
28+
```
29+
$ faas up -f <fn-name>.yml
30+
```
31+
Test the new function
32+
```
33+
$ echo -n content | faas invoke <fn-name>
34+
```
2335

36+
# Using the python3-http templates
37+
Create a new function
38+
```
39+
$ faas new --lang python3-http <fn-name>
40+
```
41+
Build, push, and deploy
42+
```
43+
$ faas up -f <fn-name>.yml
44+
```
45+
Set your OpenFaaS gateway URL. For example:
2446
```
25-
faas build \
26-
&& faas deploy
27-
&& faas list --verbose
47+
$ OPENFAAS_URL=http://127.0.0.1:8080
48+
```
49+
Test the new function
50+
```
51+
$ curl -i $OPENFAAS_URL/function/<fn-name>
52+
```
53+
54+
## Event and Context Data
55+
The function handler is passed two arguments, *event* and *context*.
56+
57+
*event* contains data about the request, including:
58+
- body
59+
- headers
60+
- method
61+
- query
62+
- path
63+
64+
*context* contains basic information about the function, including:
65+
- hostname
66+
67+
## Response Bodies
68+
By default, flask will automatically attempt to set the correct Content-Type header for you based on the type of response.
2869

29-
# Wait a couple of seconds then:
70+
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.
3071

31-
echo -n content | faas invoke myfunction
72+
## Example usage
73+
### Custom Status Codes and Response Bodies
74+
Successful response status code and JSON response body
75+
```python
76+
def handle(event, context):
77+
return {
78+
"statusCode": 200,
79+
"body": {
80+
"key": "value"
81+
}
82+
}
83+
```
84+
Successful response status code and string response body
85+
```python
86+
def handle(event, context):
87+
return {
88+
"statusCode": 201,
89+
"body": "Object successfully created"
90+
}
91+
```
92+
Failure response status code and JSON error message
93+
```python
94+
def handle(event, context):
95+
return {
96+
"statusCode": 400,
97+
"body": {
98+
"error": "Bad request"
99+
}
100+
}
101+
```
102+
### Custom Response Headers
103+
Setting custom response headers
104+
```python
105+
def handle(event, context):
106+
return {
107+
"statusCode": 200,
108+
"body": {
109+
"key": "value"
110+
},
111+
"headers": {
112+
"Location": "https://www.example.com/"
113+
}
114+
}
115+
```
116+
### Accessing Event Data
117+
Accessing request body
118+
```python
119+
def handle(event, context):
120+
return {
121+
"statusCode": 200,
122+
"body": "You said: " + str(event.body)
123+
}
124+
```
125+
Accessing request method
126+
```python
127+
def handle(event, context):
128+
if event.method == 'GET':
129+
return {
130+
"statusCode": 200,
131+
"body": "GET request"
132+
}
133+
else:
134+
return {
135+
"statusCode": 405,
136+
"body": "Method not allowed"
137+
}
138+
```
139+
Accessing request query string arguments
140+
```python
141+
def handle(event, context):
142+
return {
143+
"statusCode": 200,
144+
"body": {
145+
"name": event.query['name']
146+
}
147+
}
32148
```
149+
Accessing request headers
150+
```python
151+
def handle(event, context):
152+
return {
153+
"statusCode": 200,
154+
"body": {
155+
"content-type-received": event.headers['Content-Type']
156+
}
157+
}
158+
```
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
FROM armhf/python:3.6-alpine
2+
3+
ARG ADDITIONAL_PACKAGE
4+
# Alternatively use ADD https:// (which will not be cached by Docker builder)
5+
RUN apk --no-cache add curl ${ADDITIONAL_PACKAGE} \
6+
&& echo "Pulling watchdog binary from Github." \
7+
&& curl -sSLf https://github.com/openfaas-incubator/of-watchdog/releases/download/0.4.6/of-watchdog-armhf > /usr/bin/fwatchdog \
8+
&& chmod +x /usr/bin/fwatchdog \
9+
&& apk del curl --no-cache
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 index.py .
22+
COPY requirements.txt .
23+
USER root
24+
RUN pip install -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 function/requirements.txt .
31+
RUN pip install --user -r requirements.txt
32+
33+
WORKDIR /home/app/
34+
35+
USER root
36+
COPY function function
37+
RUN chown -R app:app ./
38+
USER app
39+
40+
# Set up of-watchdog for HTTP mode
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+
HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1
47+
48+
CMD ["fwatchdog"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def handle(event, context):
2+
# TODO implement
3+
return {
4+
"statusCode": 200,
5+
"body": "Hello from OpenFaaS!"
6+
}

template/python3-http-armhf/function/requirements.txt

Whitespace-only changes.

template/python3-http-armhf/index.py

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from flask import Flask, request, jsonify
2+
from waitress import serve
3+
import os
4+
5+
from function import handler
6+
7+
app = Flask(__name__)
8+
9+
class Event:
10+
def __init__(self):
11+
self.body = request.get_data()
12+
self.headers = request.headers
13+
self.method = request.method
14+
self.query = request.args
15+
self.path = request.path
16+
17+
class Context:
18+
def __init__(self):
19+
self.hostname = os.environ['HOSTNAME']
20+
21+
def format_status_code(resp):
22+
if 'statusCode' in resp:
23+
return resp['statusCode']
24+
25+
return 200
26+
27+
def format_body(resp):
28+
if 'body' not in resp:
29+
return ""
30+
elif type(resp['body']) == dict:
31+
return jsonify(resp['body'])
32+
else:
33+
return str(resp['body'])
34+
35+
def format_headers(resp):
36+
if 'headers' not in resp:
37+
return []
38+
elif type(resp['headers']) == dict:
39+
headers = []
40+
for key in resp['headers'].keys():
41+
header_tuple = (key, resp['headers'][key])
42+
headers.append(header_tuple)
43+
return headers
44+
45+
return resp['headers']
46+
47+
def format_response(resp):
48+
if resp == None:
49+
return ('', 200)
50+
51+
statusCode = format_status_code(resp)
52+
body = format_body(resp)
53+
headers = format_headers(resp)
54+
55+
return (body, statusCode, headers)
56+
57+
@app.route('/', defaults={'path': ''}, methods=['GET', 'PUT', 'POST', 'PATCH', 'DELETE'])
58+
@app.route('/<path:path>', methods=['GET', 'PUT', 'POST', 'PATCH' 'DELETE'])
59+
def call_handler(path):
60+
event = Event()
61+
context = Context()
62+
response_data = handler.handle(event, context)
63+
64+
resp = format_response(response_data)
65+
return resp
66+
67+
if __name__ == '__main__':
68+
serve(app, host='0.0.0.0', port=5000)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
flask
2+
waitress
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
language: python3-http-armhf
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

template/python3-http/Dockerfile

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
FROM python:3.7-alpine
2+
3+
ARG ADDITIONAL_PACKAGE
4+
# Alternatively use ADD https:// (which will not be cached by Docker builder)
5+
RUN apk --no-cache add curl ${ADDITIONAL_PACKAGE} \
6+
&& echo "Pulling watchdog binary from Github." \
7+
&& curl -sSLf https://github.com/openfaas-incubator/of-watchdog/releases/download/0.4.6/of-watchdog > /usr/bin/fwatchdog \
8+
&& chmod +x /usr/bin/fwatchdog \
9+
&& apk del curl --no-cache
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 index.py .
22+
COPY requirements.txt .
23+
USER root
24+
RUN pip install -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 function/requirements.txt .
31+
RUN pip install --user -r requirements.txt
32+
33+
WORKDIR /home/app/
34+
35+
USER root
36+
COPY function function
37+
RUN chown -R app:app ./
38+
USER app
39+
40+
# Set up of-watchdog for HTTP mode
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+
HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1
47+
48+
CMD ["fwatchdog"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def handle(event, context):
2+
# TODO implement
3+
return {
4+
"statusCode": 200,
5+
"body": "Hello from OpenFaaS!"
6+
}

template/python3-http/function/requirements.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)