Skip to content

Commit 16941a1

Browse files
authored
feat: python version of idp-sql tutorial (#5315)
* Python version of idp-sql tutorial
1 parent 5da6c9e commit 16941a1

22 files changed

+1370
-0
lines changed

run/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This directory contains samples for [Google Cloud Run](https://cloud.run). [Clou
1616
|[Cloud SQL (MySQL)][mysql] | Use MySQL with Cloud Run | - |
1717
|[Cloud SQL (Postgres)][postgres] | Use Postgres with Cloud Run | - |
1818
|[Django][django] | Deploy Django on Cloud Run | - |
19+
|[Identity Platform][idp-sql] | Authenticate users and connect to a Cloud SQL postgreSQL databases | [<img src="https://storage.googleapis.com/cloudrun/button.svg" alt="Run on Google Cloud" height="30">][run_button_idpsql] |
1920

2021
For more Cloud Run samples beyond Python, see the main list in the [Cloud Run Samples repository](https://github.com/GoogleCloudPlatform/cloud-run-samples).
2122

@@ -111,6 +112,8 @@ for more information.
111112
[mysql]: ../cloud-sql/mysql/sqlalchemy
112113
[postgres]: ../cloud-sql/postgres/sqlalchemy
113114
[django]: django/
115+
[idp-sql]: idp-sql/
114116
[run_button_helloworld]: https://deploy.cloud.run/?git_repo=https://github.com/knative/docs&dir=docs/serving/samples/hello-world/helloworld-python
115117
[run_button_pubsub]: https://deploy.cloud.run/?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&dir=run/pubsub
118+
[run_button_idpsql]: https://deploy.cloud.run/?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&dir=run/idp-sql
116119
[testing]: https://cloud.google.com/run/docs/testing/local#running_locally_using_docker_with_access_to_services

run/idp-sql/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2021 Google LLC. All rights reserved.
2+
# Use of this source code is governed by the Apache 2.0
3+
# license that can be found in the LICENSE file.
4+
5+
# Use the official Python image.
6+
# https://hub.docker.com/_/python
7+
FROM python:3.9
8+
9+
# Send stdout/stderr out, do not buffer.
10+
ENV PYTHONUNBUFFERED 1
11+
12+
# Copy application dependency manifests to the container image.
13+
# Copying this separately prevents re-running pip install on every code change.
14+
COPY requirements.txt ./
15+
16+
# Install production dependencies.
17+
RUN set -ex; \
18+
pip install -r requirements.txt;
19+
20+
# Copy local code to the container image.
21+
ENV APP_HOME /app
22+
WORKDIR $APP_HOME
23+
COPY . ./
24+
25+
# Run the web service on container startup. Here we use the gunicorn
26+
# webserver, with one worker process and 8 threads.
27+
# For environments with multiple CPU cores, increase the number of workers
28+
# to be equal to the cores available.
29+
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

run/idp-sql/README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Cloud Run End User Authentication with PostgreSQL Database Sample
2+
3+
This sample integrates with the Identity Platform to authenticate users to the
4+
application and connects to a Cloud SQL postgreSQL database for data storage.
5+
6+
Use it with the [End user Authentication for Cloud Run](http://cloud.google.com/run/docs/tutorials/identity-platform).
7+
8+
For more details on how to work with this sample read the [Google Cloud Run Python Samples README](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/run).
9+
10+
[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run)
11+
12+
## Dependencies
13+
14+
* **flask**: web server framework
15+
* **google-cloud-secret-manager**: Google Secret Manager client library
16+
* **firebase-admin**: verifying JWT token
17+
* **sqlalchemy + pg8000**: postgresql interface
18+
* **Firebase JavaScript SDK**: client-side library for authentication flow
19+
20+
## Environment Variables
21+
22+
Cloud Run services can be [configured with Environment Variables](https://cloud.google.com/run/docs/configuring/environment-variables).
23+
Required variables for this sample include:
24+
25+
* `CLOUD_SQL_CREDENTIALS_SECRET`: the resource ID of the secret, in format: `projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION`. See [postgres-secrets.json](postgres-secrets.json) for secret content.
26+
27+
OR
28+
29+
* `CLOUD_SQL_CONNECTION_NAME`: Cloud SQL instance name, in format: `<MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>`
30+
* `DB_NAME`: Cloud SQL postgreSQL database name
31+
* `DB_USER`: database user
32+
* `DB_PASSWORD`: database password
33+
34+
Other environment variables:
35+
36+
* Set `TABLE` to change the postgreSQL database table name.
37+
38+
* Set `DB_HOST` to use the proxy with TCP. See instructions below.
39+
40+
* Set `DB_SOCKET_PATH` to change the directory when using the proxy with Unix sockets.
41+
See instructions below.
42+
43+
## Production Considerations
44+
45+
* Both `postgres-secrets.json` and `static/config.js` should not be committed to
46+
a git repository and should be added to `.gitignore`.
47+
48+
* Saving credentials directly as environment variables is convenient for local testing,
49+
but not secure for production; therefore using `CLOUD_SQL_CREDENTIALS_SECRET`
50+
in combination with the Cloud Secrets Manager is recommended.
51+
52+
## Running Locally
53+
54+
1. Set [environment variables](#environment-variables).
55+
56+
1. To run this application locally, download and install the `cloud_sql_proxy` by
57+
[following the instructions](https://cloud.google.com/sql/docs/postgres/sql-proxy#install).
58+
59+
The proxy can be used with a TCP connection or a Unix Domain Socket. On Linux or
60+
Mac OS you can use either option, but on Windows the proxy currently requires a TCP
61+
connection.
62+
63+
[Instructions to launch proxy with Unix Domain Socket](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/cloud-sql/postgres/sqlalchemy#launch-proxy-with-unix-domain-socket)
64+
65+
[Instructions to launch proxy with TCP](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/cloud-sql/postgres/sqlalchemy#launch-proxy-with-tcp)
66+
67+
68+
## Testing
69+
70+
Tests expect the Cloud SQL instance to already be created and environment Variables
71+
to be set.
72+
73+
### Unit tests
74+
75+
```
76+
pytest test/test_app.py
77+
```
78+
79+
### System Tests
80+
81+
```
82+
export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID>
83+
export CLOUD_SQL_CONNECTION_NAME=<YOUR_CLOUD_SQL_CONNECTION_NAME>
84+
export DB_PASSWORD=<POSTGRESQL_PASSWORD>
85+
export IDP_KEY=<IDENTITY_PLATFORM_API_KEY> # See tutorial for creation of this key ("API_KEY")
86+
pytest test/e2e_test.py
87+
```

run/idp-sql/app.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "idp-sql",
3+
"env": {
4+
"DB_PASSWORD": {
5+
"description": "postgreSQL password for root user"
6+
},
7+
"CLOUD_SQL_INSTANCE_NAME": {
8+
"description": "Cloud SQL instance name",
9+
"value": "idp-sql-instance"
10+
},
11+
"API_KEY": {
12+
"description": "Identity Platform API key from Application Setup Details"
13+
}
14+
},
15+
"hooks": {
16+
"precreate": {
17+
"commands": [
18+
"./setup.sh"
19+
]
20+
},
21+
"postcreate": {
22+
"commands": [
23+
"./postcreate.sh"
24+
]
25+
}
26+
}
27+
}

run/idp-sql/credentials.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2021 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 json
16+
import os
17+
from typing import Dict
18+
19+
from google.cloud import secretmanager
20+
21+
from middleware import logger
22+
23+
24+
# [START cloudrun_user_auth_secrets]
25+
def get_cred_config() -> Dict[str, str]:
26+
if "CLOUD_SQL_CREDENTIALS_SECRET" in os.environ:
27+
name = os.environ["CLOUD_SQL_CREDENTIALS_SECRET"]
28+
client = secretmanager.SecretManagerServiceClient()
29+
response = client.access_secret_version(request={"name": name})
30+
logger.info("Credentials pulled from CLOUD_SQL_CREDENTIALS_SECRET")
31+
return json.loads(response.payload.data.decode("UTF-8"))
32+
# [END cloudrun_user_auth_secrets]
33+
else:
34+
logger.info(
35+
"CLOUD_SQL_CREDENTIALS_SECRET env var not set. Defaulting to environment variables."
36+
)
37+
if "DB_USER" not in os.environ:
38+
raise Exception("DB_USER needs to be set.")
39+
40+
if "DB_PASSWORD" not in os.environ:
41+
raise Exception("DB_PASSWORD needs to be set.")
42+
43+
if "DB_NAME" not in os.environ:
44+
raise Exception("DB_NAME needs to be set.")
45+
46+
if "CLOUD_SQL_CONNECTION_NAME" not in os.environ:
47+
raise Exception("CLOUD_SQL_CONNECTION_NAME needs to be set.")
48+
49+
return {
50+
"DB_USER": os.environ["DB_USER"],
51+
"DB_PASSWORD": os.environ["DB_PASSWORD"],
52+
"DB_NAME": os.environ["DB_NAME"],
53+
"DB_HOST": os.environ.get("DB_HOST", None),
54+
"CLOUD_SQL_CONNECTION_NAME": os.environ["CLOUD_SQL_CONNECTION_NAME"],
55+
}

0 commit comments

Comments
 (0)