Skip to content

Commit 313cc87

Browse files
authored
feat: add eventarc generic sample and other eventarc (GoogleCloudPlatform#4845)
## Description - Adds running locally instructions like Run - Simplifies Eventarc samples. Does not use CE SDK – blocked by cloudevents/sdk-python#126 - Adds `eventarc/generic` sample Fixes GoogleCloudPlatform#4834 Ran `nox -s lint`
1 parent c4132c0 commit 313cc87

14 files changed

+427
-91
lines changed

eventarc/README.md

+28-1
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,33 @@ For more Cloud Run samples beyond Python, see the main list in the [Cloud Run Sa
3030
3131
Note: Some samples in the list above are hosted in other repositories. They are noted with the symbol "➥".
3232
33-
3433
## How to run a sample locally
3534
35+
Install [`pip`][pip] and [`virtualenv`][virtualenv] if you do not already have them.
36+
37+
You may want to refer to the [`Python Development Environment Setup Guide`][setup] for Google Cloud Platform for instructions.
38+
39+
1. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+.
40+
41+
```sh
42+
virtualenv env
43+
source env/bin/activate
44+
```
45+
46+
1. Install the dependencies needed to run the samples.
47+
48+
```sh
49+
pip install -r requirements.txt
50+
```
51+
52+
1. Start the application
53+
54+
```sh
55+
python main.py
56+
```
57+
58+
## How to run a sample in a container
59+
3660
1. [Install docker locally](https://docs.docker.com/install/)
3761
3862
2. [Build the sample container](https://cloud.google.com/run/docs/building/containers#building_locally_and_pushing_using_docker):
@@ -112,3 +136,6 @@ for more information.
112136
[events_storage]: audit-storage/README.md
113137
[anthos_events_storage]: audit-storage/anthos.md
114138
[testing]: https://cloud.google.com/run/docs/testing/local#running_locally_using_docker_with_access_to_services
139+
[setup]: https://cloud.google.com/python/setup
140+
[pip]: https://pip.pypa.io/
141+
[virtualenv]: https://virtualenv.pypa.io/

eventarc/audit-storage/main.py

+5-26
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,22 @@
1515
# [START eventarc_gcs_server]
1616
import os
1717

18-
import cloudevents.exceptions as cloud_exceptions
19-
from cloudevents.http import from_http
20-
2118
from flask import Flask, request
2219

2320

24-
required_fields = ['Ce-Id', 'Ce-Source', 'Ce-Type', 'Ce-Specversion']
2521
app = Flask(__name__)
2622
# [END eventarc_gcs_server]
2723

2824

2925
# [START eventarc_gcs_handler]
3026
@app.route('/', methods=['POST'])
3127
def index():
32-
# Create CloudEvent from HTTP headers and body
33-
try:
34-
event = from_http(request.headers, request.get_data())
35-
36-
except cloud_exceptions.MissingRequiredFields as e:
37-
print(f"cloudevents.exceptions.MissingRequiredFields: {e}")
38-
return "Failed to find all required cloudevent fields. ", 400
39-
40-
except cloud_exceptions.InvalidStructuredJSON as e:
41-
print(f"cloudevents.exceptions.InvalidStructuredJSON: {e}")
42-
return "Could not deserialize the payload as JSON. ", 400
43-
44-
except cloud_exceptions.InvalidRequiredFields as e:
45-
print(f"cloudevents.exceptions.InvalidRequiredFields: {e}")
46-
return "Request contained invalid required cloudevent fields. ", 400
47-
48-
if 'subject' not in event:
49-
errmsg = 'Bad Request: expected header ce-subject'
50-
print(errmsg)
51-
return errmsg, 400
28+
# Gets the GCS bucket name from the CloudEvent header
29+
# Example: "storage.googleapis.com/projects/_/buckets/my-bucket"
30+
bucket = request.headers.get('ce-subject')
5231

53-
print(f"Detected change in GCS bucket: {event['subject']}")
54-
return (f"Detected change in GCS bucket: {event['subject']}", 200)
32+
print(f"Detected change in GCS bucket: {bucket}")
33+
return (f"Detected change in GCS bucket: {bucket}", 200)
5534
# [END eventarc_gcs_handler]
5635

5736

eventarc/audit-storage/main_test.py

-20
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,3 @@ def test_endpoint(client, capsys):
4343

4444
out, _ = capsys.readouterr()
4545
assert f"Detected change in GCS bucket: {test_headers['Ce-Subject']}" in out
46-
47-
48-
def test_missing_subject(client, capsys):
49-
r = client.post('/', headers=binary_headers)
50-
assert r.status_code == 400
51-
52-
out, _ = capsys.readouterr()
53-
assert 'Bad Request: expected header ce-subject' in out
54-
55-
56-
def test_missing_required_fields(client, capsys):
57-
for field in binary_headers:
58-
test_headers = copy.copy(binary_headers)
59-
test_headers.pop(field)
60-
61-
r = client.post('/', headers=test_headers)
62-
assert r.status_code == 400
63-
64-
out, _ = capsys.readouterr()
65-
assert 'MissingRequiredFields' in out
-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
Flask==1.1.2
22
gunicorn==20.0.4
3-
cloudevents==1.2.0

eventarc/generic/.dockerignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Dockerfile
2+
README.md
3+
*.pyc
4+
*.pyo
5+
*.pyd
6+
__pycache__

eventarc/generic/Dockerfile

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright 2020 Google, LLC.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START eventarc_generic_dockerfile]
16+
17+
# Use the official Python image.
18+
# https://hub.docker.com/_/python
19+
FROM python:3.8-slim
20+
21+
# Allow statements and log messages to immediately appear in the Cloud Run logs
22+
ENV PYTHONUNBUFFERED True
23+
24+
# Copy application dependency manifests to the container image.
25+
# Copying this separately prevents re-running pip install on every code change.
26+
COPY requirements.txt ./
27+
28+
# Install production dependencies.
29+
RUN pip install -r requirements.txt
30+
31+
# Copy local code to the container image.
32+
ENV APP_HOME /app
33+
WORKDIR $APP_HOME
34+
COPY . ./
35+
36+
# Run the web service on container startup.
37+
# Use gunicorn webserver with one worker process and 8 threads.
38+
# For environments with multiple CPU cores, increase the number of workers
39+
# to be equal to the cores available.
40+
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
41+
# [END eventarc_generic_dockerfile]

eventarc/generic/main.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright 2020 Google, LLC.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START eventarc_generic_server]
16+
import os
17+
18+
from flask import Flask, request
19+
20+
21+
app = Flask(__name__)
22+
# [END eventarc_generic_server]
23+
24+
25+
# [START eventarc_generic_handler]
26+
@app.route('/', methods=['POST'])
27+
def index():
28+
print('Event received!')
29+
30+
print('HEADERS:')
31+
headers = dict(request.headers)
32+
headers.pop('Authorization', None) # do not log authorization header if exists
33+
print(headers)
34+
35+
print('BODY:')
36+
body = dict(request.json)
37+
print(body)
38+
39+
resp = {
40+
"headers": headers,
41+
"body": body
42+
}
43+
return (resp, 200)
44+
# [END eventarc_generic_handler]
45+
46+
47+
# [START eventarc_generic_server]
48+
if __name__ == "__main__":
49+
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
50+
# [END eventarc_generic_server]

eventarc/generic/main_test.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright 2020 Google, LLC.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from uuid import uuid4
15+
16+
import pytest
17+
18+
import main
19+
20+
21+
binary_headers = {
22+
"ce-id": str(uuid4),
23+
"ce-type": "com.pytest.sample.event",
24+
"ce-source": "<my-test-source>",
25+
"ce-specversion": "1.0"
26+
}
27+
28+
29+
@pytest.fixture
30+
def client():
31+
32+
main.app.testing = True
33+
return main.app.test_client()
34+
35+
36+
def test_relay(client, capsys):
37+
38+
r = client.post('/', json={'message': {'data': 'Hello'}}, headers=binary_headers)
39+
assert r.status_code == 200
40+
41+
out, _ = capsys.readouterr()
42+
43+
# Assert print
44+
assert 'Event received!' in out
45+
46+
# Ensure output relays HTTP headers
47+
assert '"Ce-Specversion":"1.0"' in r.data.decode('utf-8')
48+
49+
# Ensure output relays HTTP body
50+
assert binary_headers['ce-id'] in out
51+
assert "{'message': {'data': 'Hello'}}" in out
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest==6.0.1

eventarc/generic/requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Flask==1.1.2
2+
gunicorn==20.0.4

0 commit comments

Comments
 (0)