Skip to content

Commit a09ca74

Browse files
dinagravesbusunkim96
authored andcommitted
Adding Cloud run system package sample (#2554)
1 parent cce0c9d commit a09ca74

File tree

8 files changed

+230
-0
lines changed

8 files changed

+230
-0
lines changed

noxfile-template.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ def _setup_appengine_sdk(session):
149149
or str(Path(sample).absolute().relative_to(REPO_ROOT)).startswith(
150150
"bigquery/pandas-gbq-migration"
151151
)
152+
or str(Path(sample).absolute().relative_to(REPO_ROOT)).startswith(
153+
"run/system-package"
154+
)
152155
)
153156
]
154157
NON_GAE_STANDARD_SAMPLES_PY2 = sorted(
@@ -207,6 +210,7 @@ def py3(session, sample):
207210
"""Runs py.test for a sample using Python 3.x"""
208211
_session_tests(session, sample)
209212

213+
210214
@nox.session(python="3.6")
211215
def lint(session):
212216
session.install("flake8", "flake8-import-order")
@@ -219,6 +223,7 @@ def lint(session):
219223
]
220224
session.run("flake8", *args)
221225

226+
222227
SAMPLES_WITH_GENERATED_READMES = sorted(list(_collect_dirs(".", suffix=".rst.in")))
223228

224229

noxfile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def _setup_appengine_sdk(session):
174174
sample.startswith("./appengine/standard_python37")
175175
or sample.startswith("./functions/")
176176
or sample.startswith("./bigquery/pandas-gbq-migration")
177+
or sample.startswith("./run/system-package")
177178
)
178179
]
179180
NON_GAE_STANDARD_SAMPLES_PY2 = sorted(

run/system-package/.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Dockerfile
2+
.dockerignore
3+
__pycache__
4+
.pytest_cache

run/system-package/Dockerfile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2019 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+
# Use the official Python image.
16+
# https://hub.docker.com/_/python
17+
FROM python:3.7
18+
19+
# [START run_system_package_ubuntu]
20+
RUN apt-get update -y && apt-get install -y \
21+
graphviz \
22+
&& apt-get clean
23+
# [END run_system_package_ubuntu]
24+
25+
# Copy application dependency manifests to the container image.
26+
# Copying this separately prevents re-running pip install on every code change.
27+
COPY requirements.txt .
28+
29+
# Install production dependencies.
30+
RUN pip install -r requirements.txt
31+
32+
# Copy local code to the container image.
33+
ENV APP_HOME /app
34+
WORKDIR $APP_HOME
35+
COPY . .
36+
37+
# Run the web service on container startup.
38+
# Use gunicorn webserver with one worker process and 8 threads.
39+
# For environments with multiple CPU cores, increase the number of workers
40+
# to be equal to the cores available.
41+
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 main:app
42+

run/system-package/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Cloud Run System Package Sample
2+
3+
This sample shows how to use a CLI tool installed as a system package as part of a web service.
4+
5+
Use it with the [Using system packages tutorial](https://cloud.google.com/run/docs/tutorials/system-packages).
6+
7+
8+
[![Run in Google Cloud][run_img]][run_link]
9+
10+
[run_img]: https://storage.googleapis.com/cloudrun/button.svg
11+
[run_link]: https://console.cloud.google.com/cloudshell/editor?shellonly=true&cloudshell_image=gcr.io/cloudrun/button&cloudshell_git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&cloudshell_working_dir=run/system-package
12+
13+
## Build
14+
15+
```
16+
docker build --tag graphviz:python .
17+
```
18+
19+
## Run Locally
20+
21+
```
22+
docker run --rm -p 9090:8080 graphviz:python
23+
```
24+
25+
## Test
26+
27+
```
28+
pytest
29+
```
30+
31+
_Note: you may need to install `pytest` using `pip install pytest`._
32+
33+
## Deploy
34+
35+
```sh
36+
# Set an environment variable with your GCP Project ID
37+
export GOOGLE_CLOUD_PROJECT=<PROJECT_ID>
38+
39+
# Submit a build using Google Cloud Build
40+
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/graphviz
41+
42+
# Deploy to Cloud Run
43+
gcloud beta run deploy graphviz --image gcr.io/${GOOGLE_CLOUD_PROJECT}/graphviz
44+
```

run/system-package/main.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Copyright 2019 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+
from flask import Flask, make_response, request
16+
import os
17+
import subprocess
18+
import sys
19+
20+
21+
app = Flask(__name__)
22+
23+
24+
# [START run_system_package_handler]
25+
@app.route("/diagram.png", methods=["GET"])
26+
def index():
27+
# Takes an HTTP GET request with query param dot and
28+
# returns a png with the rendered DOT diagram in a HTTP response.
29+
try:
30+
image = create_diagram(request.args.get("dot"))
31+
response = make_response(image)
32+
response.headers.set("Content-Type", "image/png")
33+
return response
34+
35+
except Exception as e:
36+
print("error: {}".format(e))
37+
38+
# Flush the stdout to avoid log buffering.
39+
sys.stdout.flush()
40+
41+
# If no graphviz definition or bad graphviz def, return 400
42+
if "syntax" in str(e):
43+
return "Bad Request: {}".format(e), 400
44+
45+
return "Internal Server Error", 500
46+
47+
48+
# [END run_system_package_handler]
49+
50+
51+
# [START run_system_package_exec]
52+
def create_diagram(dot):
53+
# Generates a diagram based on a graphviz DOT diagram description.
54+
if not dot:
55+
raise Exception("syntax: no graphviz definition provided")
56+
57+
dot_args = [ # These args add a watermark to the dot graphic.
58+
"-Glabel=Made on Cloud Run",
59+
"-Gfontsize=10",
60+
"-Glabeljust=right",
61+
"-Glabelloc=bottom",
62+
"-Gfontcolor=gray",
63+
"-Tpng",
64+
]
65+
66+
# Uses local `dot` binary from Graphviz:
67+
# https://graphviz.gitlab.io
68+
image = subprocess.run(
69+
["dot"] + dot_args, input=dot.encode("utf-8"), stdout=subprocess.PIPE
70+
).stdout
71+
72+
if not image:
73+
raise Exception("syntax: bad graphviz definition provided")
74+
return image
75+
76+
77+
# [END run_system_package_exec]
78+
79+
80+
if __name__ == "__main__":
81+
PORT = int(os.getenv("PORT")) if os.getenv("PORT") else 8080
82+
83+
# This is used when running locally. Gunicorn is used to run the
84+
# application on Cloud Run. See entrypoint in Dockerfile.
85+
app.run(host="127.0.0.1", port=PORT, debug=True)

run/system-package/main_test.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright 2019 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+
# NOTE:
16+
# To pass these tests locally, run `brew install graphviz`
17+
18+
19+
import main
20+
import pytest
21+
22+
23+
@pytest.fixture
24+
def client():
25+
main.app.testing = True
26+
return main.app.test_client()
27+
28+
29+
def test_empty_query_string(client):
30+
r = client.get("/diagram.png")
31+
assert r.status_code == 400
32+
33+
34+
def test_empty_dot_parameter(client):
35+
r = client.get("/diagram.png?dot=")
36+
assert r.status_code == 400
37+
38+
39+
def test_bad_dot_parameter(client):
40+
r = client.get("/diagram.png?dot=digraph")
41+
assert r.status_code == 400
42+
43+
44+
def test_good_dot_parameter(client):
45+
r = client.get("/diagram.png?dot=digraph G { A -> {B, C, D} -> {F} }")
46+
assert r.content_type == "image/png"

run/system-package/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Flask==1.1.1
2+
pytest==5.1.3
3+
gunicorn==19.9.0

0 commit comments

Comments
 (0)