Skip to content

Commit 82da184

Browse files
authored
Cloud Run Hello World (#4428)
* Cloud Run Hello World * lint * updates * Update ignore files
1 parent 019ff9c commit 82da184

File tree

12 files changed

+344
-1
lines changed

12 files changed

+344
-1
lines changed

run/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ for more information.
109109
[run_docs]: https://cloud.google.com/run/docs/
110110
[run_build]: https://cloud.google.com/run/docs/building/containers
111111
[run_deploy]: https://cloud.google.com/run/docs/deploying
112-
[helloworld]: https://github.com/knative/docs/tree/master/docs/serving/samples/hello-world/helloworld-python
112+
[helloworld]: helloworld/
113113
[pubsub]: pubsub/
114114
[mysql]: ../cloud-sql/mysql/sqlalchemy
115115
[postgres]: ../cloud-sql/postgres/sqlalchemy

run/helloworld/.dockerignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Dockerfile
2+
README.md
3+
*.pyc
4+
*.pyo
5+
*.pyd
6+
__pycache__
7+
.pytest_cache

run/helloworld/.gcloudignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
README.md
2+
*.pyc
3+
*.pyo
4+
*.pyd
5+
__pycache__
6+
.pytest_cache

run/helloworld/Dockerfile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 run_hello_dockerfile]
16+
17+
# Use the official lightweight 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 Knative logs
22+
ENV PYTHONUNBUFFERED True
23+
24+
# Copy local code to the container image.
25+
ENV APP_HOME /app
26+
WORKDIR $APP_HOME
27+
COPY . ./
28+
29+
# Install production dependencies.
30+
RUN pip install Flask gunicorn
31+
32+
# Run the web service on container startup. Here we use the gunicorn
33+
# webserver, with one worker process and 8 threads.
34+
# For environments with multiple CPU cores, increase the number of workers
35+
# to be equal to the cores available.
36+
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
37+
38+
# [END run_hello_dockerfile]

run/helloworld/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Cloud Run Hello World Sample
2+
3+
This sample shows how to deploy a Hello World application to Cloud Run.
4+
5+
[![Run in Google Cloud][run_img]][run_link]
6+
7+
[run_img]: https://storage.googleapis.com/cloudrun/button.svg
8+
[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/helloworld
9+
10+
## Build
11+
12+
```
13+
docker build --tag helloworld:python .
14+
```
15+
16+
## Run Locally
17+
18+
```
19+
docker run --rm -p 9090:8080 -e PORT=8080 helloworld:python
20+
```
21+
22+
## Test
23+
24+
```
25+
pytest
26+
```
27+
28+
_Note: you may need to install `pytest` using `pip install pytest`._
29+
30+
## Deploy
31+
32+
```sh
33+
# Set an environment variable with your GCP Project ID
34+
export GOOGLE_CLOUD_PROJECT=<PROJECT_ID>
35+
36+
# Submit a build using Google Cloud Build
37+
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/helloworld
38+
39+
# Deploy to Cloud Run
40+
gcloud run deploy helloworld \
41+
--image gcr.io/${GOOGLE_CLOUD_PROJECT}/helloworld
42+
```
43+
44+
45+
For more details on how to work with this sample read the [Python Cloud Run Samples README](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/run)

run/helloworld/e2e_test.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
# This sample creates a secure two-service application running on Cloud Run.
16+
# This test builds and deploys the two secure services
17+
# to test that they interact properly together.
18+
19+
import os
20+
import subprocess
21+
from urllib import request
22+
import uuid
23+
24+
import pytest
25+
26+
27+
@pytest.fixture()
28+
def services():
29+
# Unique suffix to create distinct service names
30+
suffix = uuid.uuid4().hex
31+
project = os.environ["GOOGLE_CLOUD_PROJECT"]
32+
33+
# Build and Deploy Cloud Run Services
34+
subprocess.run(
35+
[
36+
"gcloud",
37+
"builds",
38+
"submit",
39+
f"--project={project}",
40+
f"--substitutions=_SUFFIX={suffix}",
41+
"--config=e2e_test_setup.yaml",
42+
"--quiet",
43+
],
44+
check=True,
45+
)
46+
47+
# Get the URL for the service and the token
48+
service = subprocess.run(
49+
[
50+
"gcloud",
51+
"run",
52+
f"--project={project}",
53+
"--platform=managed",
54+
"--region=us-central1",
55+
"services",
56+
"describe",
57+
f"helloworld-{suffix}",
58+
"--format=value(status.url)",
59+
],
60+
stdout=subprocess.PIPE,
61+
check=True,
62+
).stdout.strip()
63+
64+
token = subprocess.run(
65+
["gcloud", "auth", "print-identity-token"], stdout=subprocess.PIPE, check=True
66+
).stdout.strip()
67+
68+
yield service, token
69+
70+
subprocess.run(
71+
[
72+
"gcloud",
73+
"run",
74+
"services",
75+
"delete",
76+
f"helloworld-{suffix}",
77+
f"--project={project}",
78+
"--platform=managed",
79+
"--region=us-central1",
80+
"--quiet",
81+
],
82+
check=True,
83+
)
84+
85+
86+
def test_end_to_end(services):
87+
service = services[0].decode()
88+
token = services[1].decode()
89+
90+
req = request.Request(
91+
f"{service}/", headers={"Authorization": f"Bearer {token}"}
92+
)
93+
response = request.urlopen(req)
94+
assert response.status == 200
95+
96+
body = response.read()
97+
assert "Hello Test!" == body.decode()

run/helloworld/e2e_test_setup.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
steps:
16+
- # Build the renderer image
17+
name: gcr.io/cloud-builders/docker:latest
18+
args: ['build', '--tag=gcr.io/$PROJECT_ID/helloworld-${_SUFFIX}', '.']
19+
20+
- # Push the container image to Container Registry
21+
name: gcr.io/cloud-builders/docker
22+
args: ['push', 'gcr.io/$PROJECT_ID/helloworld-${_SUFFIX}']
23+
24+
- # Deploy to Cloud Run
25+
name: gcr.io/cloud-builders/gcloud
26+
args:
27+
- run
28+
- deploy
29+
- helloworld-${_SUFFIX}
30+
- --image=gcr.io/$PROJECT_ID/helloworld-${_SUFFIX}
31+
- --region=us-central1
32+
- --platform=managed
33+
- --no-allow-unauthenticated
34+
- --set-env-vars=NAME=Test
35+
36+
images:
37+
- 'gcr.io/$PROJECT_ID/helloworld-${_SUFFIX}'

run/helloworld/main.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 run_hello_service]
16+
import os
17+
18+
from flask import Flask
19+
20+
app = Flask(__name__)
21+
22+
23+
@app.route('/')
24+
def hello_world():
25+
name = os.environ.get('NAME', 'World')
26+
return 'Hello {}!'.format(name)
27+
28+
29+
if __name__ == "__main__":
30+
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
31+
# [END run_hello_service]

run/helloworld/main_test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
import os
16+
17+
import pytest
18+
19+
import main
20+
21+
22+
@pytest.fixture
23+
def client():
24+
main.app.testing = True
25+
return main.app.test_client()
26+
27+
28+
def test_handler_no_env_variable(client):
29+
r = client.get("/")
30+
31+
assert r.data.decode() == "Hello World!"
32+
assert r.status_code == 200
33+
34+
35+
def test_handler_with_env_variable(client):
36+
os.environ["NAME"] = "Foo"
37+
r = client.get("/")
38+
39+
assert r.data.decode() == "Hello Foo!"
40+
assert r.status_code == 200

run/helloworld/noxfile_config.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
# Default TEST_CONFIG_OVERRIDE for python repos.
16+
17+
# You can copy this file into your directory, then it will be inported from
18+
# the noxfile.py.
19+
20+
# The source of truth:
21+
# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/noxfile_config.py
22+
23+
TEST_CONFIG_OVERRIDE = {
24+
# You can opt out from the test for specific Python versions.
25+
26+
# We only run the cloud run tests in py38 session.
27+
'ignored_versions': ["2.7", "3.6", "3.7"],
28+
29+
# An envvar key for determining the project id to use. Change it
30+
# to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
31+
# build specific Cloud project. You can also use your own string
32+
# to use your own Cloud project.
33+
'gcloud_project_env': 'GCLOUD_PROJECT',
34+
# 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT',
35+
36+
# A dictionary you want to inject into your test. Don't put any
37+
# secrets here. These values will override predefined values.
38+
'envs': {},
39+
}

run/helloworld/requirements-test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest==6.0.1

run/helloworld/requirements.txt

Lines changed: 2 additions & 0 deletions
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)