From 60bc4fa7f2a1d434402eabfd6c0389def796e8cd Mon Sep 17 00:00:00 2001 From: kgala2 Date: Thu, 20 Nov 2025 08:21:14 +0000 Subject: [PATCH 1/5] docs: Add sample cloudrun application --- samples/cloudrun/cloudrun/README.md | 158 ++++++++++++++++++ samples/cloudrun/cloudrun/mysql/Dockerfile | 18 ++ samples/cloudrun/cloudrun/mysql/main.py | 83 +++++++++ .../cloudrun/cloudrun/mysql/requirements.txt | 5 + samples/cloudrun/cloudrun/postgres/Dockerfile | 18 ++ samples/cloudrun/cloudrun/postgres/main.py | 83 +++++++++ .../cloudrun/postgres/requirements.txt | 5 + .../cloudrun/cloudrun/sqlserver/Dockerfile | 18 ++ samples/cloudrun/cloudrun/sqlserver/main.py | 57 +++++++ .../cloudrun/sqlserver/requirements.txt | 5 + 10 files changed, 450 insertions(+) create mode 100644 samples/cloudrun/cloudrun/README.md create mode 100644 samples/cloudrun/cloudrun/mysql/Dockerfile create mode 100644 samples/cloudrun/cloudrun/mysql/main.py create mode 100644 samples/cloudrun/cloudrun/mysql/requirements.txt create mode 100644 samples/cloudrun/cloudrun/postgres/Dockerfile create mode 100644 samples/cloudrun/cloudrun/postgres/main.py create mode 100644 samples/cloudrun/cloudrun/postgres/requirements.txt create mode 100644 samples/cloudrun/cloudrun/sqlserver/Dockerfile create mode 100644 samples/cloudrun/cloudrun/sqlserver/main.py create mode 100644 samples/cloudrun/cloudrun/sqlserver/requirements.txt diff --git a/samples/cloudrun/cloudrun/README.md b/samples/cloudrun/cloudrun/README.md new file mode 100644 index 000000000..47022ae5e --- /dev/null +++ b/samples/cloudrun/cloudrun/README.md @@ -0,0 +1,158 @@ +# Connecting Cloud Run to Cloud SQL with the Python Connector + +This guide provides a comprehensive walkthrough of how to connect a Cloud Run service to a Cloud SQL instance using the Cloud SQL Python Connector. It covers connecting to instances with both public and private IP addresses and demonstrates how to handle database credentials securely. + +## Develop a Python Application + +The following Python applications demonstrate how to connect to a Cloud SQL instance using the Cloud SQL Python Connector. + +### `mysql/main.py` and `postgres/main.py` + +These files contain the core application logic for connecting to a Cloud SQL for MySQL or PostgreSQL instance. They provide two separate authentication methods, each exposed at a different route: +- `/`: Password-based authentication +- `/iam`: IAM-based authentication + + +### `sqlserver/main.py` + +This file contains the core application logic for connecting to a Cloud SQL for SQL Server instance. It uses the `cloud-sql-python-connector` to create a SQLAlchemy connection pool with password-based authentication at the `/` route. + +> [!NOTE] +> +> Cloud SQL for SQL Server does not support IAM database authentication. + + +> [!NOTE] +> **Lazy Refresh** +> +> The sample code in all three `main.py` files initializes the `Connector` with `refresh_strategy=lazy`. This is a recommended approach to avoid connection errors and optimize cost by preventing background processes from running when the CPU is throttled. + +## IAM Authentication Prerequisites + + +For IAM authentication to work, you must ensure two things: + +1. **The Cloud Run service's service account has the `Cloud SQL Client` role.** You can grant this role with the following command: + ```bash + gcloud projects add-iam-policy-binding PROJECT_ID \ + --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \ + --role="roles/cloudsql.client" + ``` + Replace `PROJECT_ID` with your Google Cloud project ID and `SERVICE_ACCOUNT_EMAIL` with the email of the service account your Cloud Run service is using. + +2. **The service account is added as a database user to your Cloud SQL instance.** You can do this with the following command: + ```bash + gcloud sql users create SERVICE_ACCOUNT_EMAIL \ + --instance=INSTANCE_NAME \ + --type=cloud_iam_user + ``` + Replace `SERVICE_ACCOUNT_EMAIL` with the same service account email and `INSTANCE_NAME` with your Cloud SQL instance name. + +## Deploy the Application to Cloud Run + +Follow these steps to deploy the application to Cloud Run. + +### Build and Push the Docker Image + +1. **Enable the Artifact Registry API:** + + ```bash + gcloud services enable artifactregistry.googleapis.com + ``` + +2. **Create an Artifact Registry repository:** + + ```bash + gcloud artifacts repositories create REPO_NAME \ + --repository-format=docker \ + --location=REGION + ``` + +3. **Configure Docker to authenticate with Artifact Registry:** + + ```bash + gcloud auth configure-docker REGION-docker.pkg.dev + ``` + +4. **Build the Docker image (replace `mysql` with `postgres` or `sqlserver` as needed):** + + ```bash + docker build -t REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME mysql + ``` + +5. **Push the Docker image to Artifact Registry:** + + ```bash + docker push REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME + ``` + +### Deploy to Cloud Run + +Deploy the container image to Cloud Run using the `gcloud run deploy` command. + + +**Sample Values:** +* `SERVICE_NAME`: `my-cloud-run-service` +* `REGION`: `us-central1` +* `PROJECT_ID`: `my-gcp-project-id` +* `REPO_NAME`: `my-artifact-repo` +* `IMAGE_NAME`: `my-app-image` +* `INSTANCE_CONNECTION_NAME`: `my-gcp-project-id:us-central1:my-instance-name` +* `DB_USER`: `my-db-user` (for password-based authentication) +* `DB_IAM_USER`: `my-service-account@my-gcp-project-id.iam.gserviceaccount.com` (for IAM-based authentication) +* `DB_NAME`: `my-db-name` +* `DB_SECRET_NAME`: `projects/my-gcp-project-id/secrets/my-db-secret/versions/latest` +* `VPC_NETWORK`: `my-vpc-network` +* `SUBNET_NAME`: `my-vpc-subnet` + + +**For MySQL and PostgreSQL (Public IP):** + +```bash +gcloud run deploy SERVICE_NAME \ + --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ + --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ + --set-env-vars=DB_USER=DB_USER,DB_IAM_USER=DB_IAM_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \ + --region=REGION +``` + +**For MySQL and PostgreSQL (Private IP):** + +```bash +gcloud run deploy SERVICE_NAME \ + --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ + --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ + --set-env-vars=DB_USER=DB_USER,DB_IAM_USER=DB_IAM_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME,IP_TYPE=PRIVATE \ + --network=VPC_NETWORK \ + --subnet=SUBNET_NAME \ + --vpc-egress=private-ranges-only \ + --region=REGION +``` + +**For SQL Server (Public IP):** + +```bash +gcloud run deploy SERVICE_NAME \ + --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ + --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ + --set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \ + --region=REGION +``` + +**For SQL Server (Private IP):** + +```bash +gcloud run deploy SERVICE_NAME \ + --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_name \ + --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ + --set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME,IP_TYPE=PRIVATE \ + --network=VPC_NETWORK \ + --subnet=SUBNET_NAME \ + --vpc-egress=private-ranges-only \ + --region=REGION +``` + +> [!NOTE] +> **`For PSC connections`** +> +> To connect to the Cloud SQL instance with PSC connection type, create a PSC endpoint, a DNS zone and DNS record for the instance in the same VPC network as the Cloud Run service and replace the `IP_TYPE` in the deploy command with `PSC`. To configure DNS records, refer to [Connect to an instance using Private Service Connect](https://docs.cloud.google.com/sql/docs/mysql/configure-private-service-connect) guide \ No newline at end of file diff --git a/samples/cloudrun/cloudrun/mysql/Dockerfile b/samples/cloudrun/cloudrun/mysql/Dockerfile new file mode 100644 index 000000000..853ca78eb --- /dev/null +++ b/samples/cloudrun/cloudrun/mysql/Dockerfile @@ -0,0 +1,18 @@ +# Use the official lightweight Python image. +# https://hub.docker.com/_/python +FROM python:3.12-slim + +# Allow statements and log messages to immediately appear in the Knative logs +ENV PYTHONUNBUFFERED True + +# Copy local code to the container image. +ENV APP_HOME /app +WORKDIR $APP_HOME +COPY . . + +# Install production dependencies. +RUN pip install --no-cache-dir -r requirements.txt + +# Run the web service on container startup. +# Use gunicorn for production deployments. +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app diff --git a/samples/cloudrun/cloudrun/mysql/main.py b/samples/cloudrun/cloudrun/mysql/main.py new file mode 100644 index 000000000..e91314ab2 --- /dev/null +++ b/samples/cloudrun/cloudrun/mysql/main.py @@ -0,0 +1,83 @@ +import os +import sqlalchemy +from flask import Flask +from google.cloud.sql.connector import Connector, IPTypes +from google.cloud import secretmanager + +# Initialize Flask app +app = Flask(__name__) + +# Initialize the Connector object with lazy refresh +connector = Connector(refresh_strategy="lazy") +secret_client = secretmanager.SecretManagerServiceClient() + +# Function to create a database connection using IAM authentication +def get_iam_connection() -> sqlalchemy.engine.base.Connection: + instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] + db_user = os.environ["DB_IAM_USER"] # IAM service account email + db_name = os.environ["DB_NAME"] + ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") + ip_type = IPTypes[ip_type_str] + + conn = connector.connect( + instance_connection_name, + "pymysql", + user=db_user, + db=db_name, + ip_type=ip_type, + enable_iam_auth=True + ) + return conn + +# Function to create a database connection using password-based authentication +def get_password_connection() -> sqlalchemy.engine.base.Connection: + instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] + db_user = os.environ["DB_USER"] # Database username + db_name = os.environ["DB_NAME"] + db_secret_name = os.environ["DB_SECRET_NAME"] + ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") + ip_type = IPTypes[ip_type_str] + + secret_response = secret_client.access_secret_version(name=db_secret_name) + db_password = secret_response.payload.data.decode("UTF-8") + + conn = connector.connect( + instance_connection_name, + "pymysql", + user=db_user, + password=db_password, + db=db_name, + ip_type=ip_type, + ) + return conn + +# Create the SQLAlchemy engines +iam_engine = sqlalchemy.create_engine( + "mysql+pymysql://", + creator=get_iam_connection, +) +password_engine = sqlalchemy.create_engine( + "mysql+pymysql://", + creator=get_password_connection, +) + +@app.route("/") +def password_auth_index(): + try: + with password_engine.connect() as conn: + result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() + return f"Database connection successful (password authentication), result: {result}" + except Exception as e: + return f"Error connecting to the database (password authentication): {e}", 500 + +@app.route("/iam") +def iam_auth_index(): + try: + with iam_engine.connect() as conn: + result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() + return f"Database connection successful (IAM authentication), result: {result}" + except Exception as e: + return f"Error connecting to the database (IAM authentication): {e}", 500 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) diff --git a/samples/cloudrun/cloudrun/mysql/requirements.txt b/samples/cloudrun/cloudrun/mysql/requirements.txt new file mode 100644 index 000000000..c21d2ff2e --- /dev/null +++ b/samples/cloudrun/cloudrun/mysql/requirements.txt @@ -0,0 +1,5 @@ +cloud-sql-python-connector[pymysql] +sqlalchemy +Flask +gunicorn +google-cloud-secret-manager diff --git a/samples/cloudrun/cloudrun/postgres/Dockerfile b/samples/cloudrun/cloudrun/postgres/Dockerfile new file mode 100644 index 000000000..853ca78eb --- /dev/null +++ b/samples/cloudrun/cloudrun/postgres/Dockerfile @@ -0,0 +1,18 @@ +# Use the official lightweight Python image. +# https://hub.docker.com/_/python +FROM python:3.12-slim + +# Allow statements and log messages to immediately appear in the Knative logs +ENV PYTHONUNBUFFERED True + +# Copy local code to the container image. +ENV APP_HOME /app +WORKDIR $APP_HOME +COPY . . + +# Install production dependencies. +RUN pip install --no-cache-dir -r requirements.txt + +# Run the web service on container startup. +# Use gunicorn for production deployments. +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app diff --git a/samples/cloudrun/cloudrun/postgres/main.py b/samples/cloudrun/cloudrun/postgres/main.py new file mode 100644 index 000000000..e61b0e4a4 --- /dev/null +++ b/samples/cloudrun/cloudrun/postgres/main.py @@ -0,0 +1,83 @@ +import os +import sqlalchemy +from flask import Flask +from google.cloud.sql.connector import Connector, IPTypes +from google.cloud import secretmanager + +# Initialize Flask app +app = Flask(__name__) + +# Initialize the Connector object with lazy refresh +connector = Connector(refresh_strategy="lazy") +secret_client = secretmanager.SecretManagerServiceClient() + +# Function to create a database connection using IAM authentication +def get_iam_connection() -> sqlalchemy.engine.base.Connection: + instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] + db_user = os.environ["DB_IAM_USER"] # IAM service account email + db_name = os.environ["DB_NAME"] + ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") + ip_type = IPTypes[ip_type_str] + + conn = connector.connect( + instance_connection_name, + "pg8000", + user=db_user, + db=db_name, + ip_type=ip_type, + enable_iam_auth=True + ) + return conn + +# Function to create a database connection using password-based authentication +def get_password_connection() -> sqlalchemy.engine.base.Connection: + instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] + db_user = os.environ["DB_USER"] # Database username + db_name = os.environ["DB_NAME"] + db_secret_name = os.environ["DB_SECRET_NAME"] + ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") + ip_type = IPTypes[ip_type_str] + + secret_response = secret_client.access_secret_version(name=db_secret_name) + db_password = secret_response.payload.data.decode("UTF-8") + + conn = connector.connect( + instance_connection_name, + "pg8000", + user=db_user, + password=db_password, + db=db_name, + ip_type=ip_type, + ) + return conn + +# Create the SQLAlchemy engines +iam_engine = sqlalchemy.create_engine( + "postgresql+pg8000://", + creator=get_iam_connection, +) +password_engine = sqlalchemy.create_engine( + "postgresql+pg8000://", + creator=get_password_connection, +) + +@app.route("/") +def password_auth_index(): + try: + with password_engine.connect() as conn: + result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() + return f"Database connection successful (password authentication), result: {result}" + except Exception as e: + return f"Error connecting to the database (password authentication): {e}", 500 + +@app.route("/iam") +def iam_auth_index(): + try: + with iam_engine.connect() as conn: + result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() + return f"Database connection successful (IAM authentication), result: {result}" + except Exception as e: + return f"Error connecting to the database (IAM authentication): {e}", 500 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) diff --git a/samples/cloudrun/cloudrun/postgres/requirements.txt b/samples/cloudrun/cloudrun/postgres/requirements.txt new file mode 100644 index 000000000..dbd9232b5 --- /dev/null +++ b/samples/cloudrun/cloudrun/postgres/requirements.txt @@ -0,0 +1,5 @@ +cloud-sql-python-connector[pg8000] +sqlalchemy +Flask +gunicorn +google-cloud-secret-manager diff --git a/samples/cloudrun/cloudrun/sqlserver/Dockerfile b/samples/cloudrun/cloudrun/sqlserver/Dockerfile new file mode 100644 index 000000000..853ca78eb --- /dev/null +++ b/samples/cloudrun/cloudrun/sqlserver/Dockerfile @@ -0,0 +1,18 @@ +# Use the official lightweight Python image. +# https://hub.docker.com/_/python +FROM python:3.12-slim + +# Allow statements and log messages to immediately appear in the Knative logs +ENV PYTHONUNBUFFERED True + +# Copy local code to the container image. +ENV APP_HOME /app +WORKDIR $APP_HOME +COPY . . + +# Install production dependencies. +RUN pip install --no-cache-dir -r requirements.txt + +# Run the web service on container startup. +# Use gunicorn for production deployments. +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app diff --git a/samples/cloudrun/cloudrun/sqlserver/main.py b/samples/cloudrun/cloudrun/sqlserver/main.py new file mode 100644 index 000000000..56e27cac9 --- /dev/null +++ b/samples/cloudrun/cloudrun/sqlserver/main.py @@ -0,0 +1,57 @@ +import os +import sqlalchemy +from flask import Flask +from google.cloud.sql.connector import Connector, IPTypes +from google.cloud import secretmanager + +# Initialize Flask app +app = Flask(__name__) + +# Initialize the Connector object with lazy refresh +connector = Connector(refresh_strategy="lazy") + +def get_connection() -> sqlalchemy.engine.base.Connection: + """ + Function to create a database connection. + This function will be used by SQLAlchemy as a creator. + """ + instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] + db_user = os.environ["DB_USER"] + db_name = os.environ["DB_NAME"] + db_secret_name = os.environ["DB_SECRET_NAME"] + ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") + ip_type = IPTypes[ip_type_str] + + # Get the database password from Secret Manager + secret_client = secretmanager.SecretManagerServiceClient() + secret_response = secret_client.access_secret_version(name=db_secret_name) + db_password = secret_response.payload.data.decode("UTF-8") + + # Connect to the database + conn = connector.connect( + instance_connection_name, + "pytds", + user=db_user, + password=db_password, + db=db_name, + ip_type=ip_type, + ) + return conn + +# Create the SQLAlchemy engine +engine = sqlalchemy.create_engine( + "mssql+pytds://", + creator=get_connection, +) + +@app.route("/") +def index(): + try: + with engine.connect() as conn: + result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() + return f"Database connection successful, result: {result}" + except Exception as e: + return f"Error connecting to the database: {e}", 500 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) diff --git a/samples/cloudrun/cloudrun/sqlserver/requirements.txt b/samples/cloudrun/cloudrun/sqlserver/requirements.txt new file mode 100644 index 000000000..e577fbdc7 --- /dev/null +++ b/samples/cloudrun/cloudrun/sqlserver/requirements.txt @@ -0,0 +1,5 @@ +cloud-sql-python-connector[pytds] +sqlalchemy-pytds +google-cloud-secret-manager +Flask +gunicorn From fdbccc8e9edfa0c228aaf7a547d042b2485de818 Mon Sep 17 00:00:00 2001 From: kgala2 Date: Thu, 20 Nov 2025 08:30:12 +0000 Subject: [PATCH 2/5] chore: update license and exception --- samples/cloudrun/cloudrun/mysql/main.py | 20 ++++++++++++++++++-- samples/cloudrun/cloudrun/postgres/main.py | 20 ++++++++++++++++++-- samples/cloudrun/cloudrun/sqlserver/main.py | 18 +++++++++++++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/samples/cloudrun/cloudrun/mysql/main.py b/samples/cloudrun/cloudrun/mysql/main.py index e91314ab2..ef8361d5c 100644 --- a/samples/cloudrun/cloudrun/mysql/main.py +++ b/samples/cloudrun/cloudrun/mysql/main.py @@ -1,3 +1,19 @@ +""" +Copyright 2025 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + import os import sqlalchemy from flask import Flask @@ -68,7 +84,7 @@ def password_auth_index(): result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (password authentication), result: {result}" except Exception as e: - return f"Error connecting to the database (password authentication): {e}", 500 + return f"Error connecting to the database (password authentication)", 500 @app.route("/iam") def iam_auth_index(): @@ -77,7 +93,7 @@ def iam_auth_index(): result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (IAM authentication), result: {result}" except Exception as e: - return f"Error connecting to the database (IAM authentication): {e}", 500 + return f"Error connecting to the database (IAM authentication)", 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) diff --git a/samples/cloudrun/cloudrun/postgres/main.py b/samples/cloudrun/cloudrun/postgres/main.py index e61b0e4a4..7be0a185d 100644 --- a/samples/cloudrun/cloudrun/postgres/main.py +++ b/samples/cloudrun/cloudrun/postgres/main.py @@ -1,3 +1,19 @@ +""" +Copyright 2025 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + import os import sqlalchemy from flask import Flask @@ -68,7 +84,7 @@ def password_auth_index(): result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (password authentication), result: {result}" except Exception as e: - return f"Error connecting to the database (password authentication): {e}", 500 + return f"Error connecting to the database (password authentication)", 500 @app.route("/iam") def iam_auth_index(): @@ -77,7 +93,7 @@ def iam_auth_index(): result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (IAM authentication), result: {result}" except Exception as e: - return f"Error connecting to the database (IAM authentication): {e}", 500 + return f"Error connecting to the database (IAM authentication)", 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) diff --git a/samples/cloudrun/cloudrun/sqlserver/main.py b/samples/cloudrun/cloudrun/sqlserver/main.py index 56e27cac9..f2f638174 100644 --- a/samples/cloudrun/cloudrun/sqlserver/main.py +++ b/samples/cloudrun/cloudrun/sqlserver/main.py @@ -1,3 +1,19 @@ +""" +Copyright 2025 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + import os import sqlalchemy from flask import Flask @@ -51,7 +67,7 @@ def index(): result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful, result: {result}" except Exception as e: - return f"Error connecting to the database: {e}", 500 + return f"Error connecting to the database", 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080))) From 14c1c7c9c8c45dbbca3af733b3e98d89762a28d2 Mon Sep 17 00:00:00 2001 From: kgala2 Date: Thu, 20 Nov 2025 18:42:06 +0000 Subject: [PATCH 3/5] chore: update documentation for lazy instantiation and global variables --- samples/cloudrun/{cloudrun => }/README.md | 12 +++ .../cloudrun/{cloudrun => }/mysql/Dockerfile | 0 samples/cloudrun/{cloudrun => }/mysql/main.py | 80 +++++++++++++++---- .../{cloudrun => }/mysql/requirements.txt | 0 .../{cloudrun => }/postgres/Dockerfile | 0 .../cloudrun/{cloudrun => }/postgres/main.py | 80 +++++++++++++++---- .../{cloudrun => }/postgres/requirements.txt | 0 .../{cloudrun => }/sqlserver/Dockerfile | 0 .../cloudrun/{cloudrun => }/sqlserver/main.py | 38 +++++++-- .../{cloudrun => }/sqlserver/requirements.txt | 0 10 files changed, 170 insertions(+), 40 deletions(-) rename samples/cloudrun/{cloudrun => }/README.md (85%) rename samples/cloudrun/{cloudrun => }/mysql/Dockerfile (100%) rename samples/cloudrun/{cloudrun => }/mysql/main.py (54%) rename samples/cloudrun/{cloudrun => }/mysql/requirements.txt (100%) rename samples/cloudrun/{cloudrun => }/postgres/Dockerfile (100%) rename samples/cloudrun/{cloudrun => }/postgres/main.py (53%) rename samples/cloudrun/{cloudrun => }/postgres/requirements.txt (100%) rename samples/cloudrun/{cloudrun => }/sqlserver/Dockerfile (100%) rename samples/cloudrun/{cloudrun => }/sqlserver/main.py (65%) rename samples/cloudrun/{cloudrun => }/sqlserver/requirements.txt (100%) diff --git a/samples/cloudrun/cloudrun/README.md b/samples/cloudrun/README.md similarity index 85% rename from samples/cloudrun/cloudrun/README.md rename to samples/cloudrun/README.md index 47022ae5e..c5d8e6279 100644 --- a/samples/cloudrun/cloudrun/README.md +++ b/samples/cloudrun/README.md @@ -27,6 +27,18 @@ This file contains the core application logic for connecting to a Cloud SQL for > > The sample code in all three `main.py` files initializes the `Connector` with `refresh_strategy=lazy`. This is a recommended approach to avoid connection errors and optimize cost by preventing background processes from running when the CPU is throttled. +## Global Variables and Lazy Instantiation + +In a Cloud Run service, global variables are initialized when the container instance starts up. The application instance then handles subsequent requests until the container is spun down. + +The `Connector` and SQLAlchemy `Engine` objects are defined as global variables (initially set to `None`) and are lazily instantiated (created only when needed) inside the request handlers. + +This approach offers several benefits: + +1. **Faster Startup:** By deferring initialization until the first request, the Cloud Run service can start listening for requests almost immediately, reducing cold start latency. +2. **Resource Efficiency:** Expensive operations, like establishing background connections or fetching secrets, are only performed when actually required. +3. **Connection Reuse:** Once initialized, the global `Connector` and `Engine` instances are reused for all subsequent requests to that container instance. This prevents the overhead of creating new connections for every request and avoids hitting connection limits. + ## IAM Authentication Prerequisites diff --git a/samples/cloudrun/cloudrun/mysql/Dockerfile b/samples/cloudrun/mysql/Dockerfile similarity index 100% rename from samples/cloudrun/cloudrun/mysql/Dockerfile rename to samples/cloudrun/mysql/Dockerfile diff --git a/samples/cloudrun/cloudrun/mysql/main.py b/samples/cloudrun/mysql/main.py similarity index 54% rename from samples/cloudrun/cloudrun/mysql/main.py rename to samples/cloudrun/mysql/main.py index ef8361d5c..ce0fdfd94 100644 --- a/samples/cloudrun/cloudrun/mysql/main.py +++ b/samples/cloudrun/mysql/main.py @@ -23,14 +23,27 @@ # Initialize Flask app app = Flask(__name__) -# Initialize the Connector object with lazy refresh -connector = Connector(refresh_strategy="lazy") +# Connector and SQLAlchemy engines are initialized as None to allow for lazy instantiation. +# +# The Connector object is a global variable to ensure that the same connector +# instance is used across all requests. This prevents the unnecessary creation +# of new Connector instances, which is inefficient and can lead to connection +# limits being reached. +# +# Lazy instantiation (initializing the Connector and Engine only when needed) +# allows the Cloud Run service to start up faster, as it avoids performing +# initialization tasks (like fetching secrets or metadata) during startup. +connector = None +iam_engine = None +password_engine = None secret_client = secretmanager.SecretManagerServiceClient() + # Function to create a database connection using IAM authentication def get_iam_connection() -> sqlalchemy.engine.base.Connection: + """Creates a database connection using IAM authentication.""" instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] - db_user = os.environ["DB_IAM_USER"] # IAM service account email + db_user = os.environ["DB_IAM_USER"] # IAM service account email db_name = os.environ["DB_NAME"] ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") ip_type = IPTypes[ip_type_str] @@ -41,14 +54,16 @@ def get_iam_connection() -> sqlalchemy.engine.base.Connection: user=db_user, db=db_name, ip_type=ip_type, - enable_iam_auth=True + enable_iam_auth=True, ) return conn + # Function to create a database connection using password-based authentication def get_password_connection() -> sqlalchemy.engine.base.Connection: + """Creates a database connection using password authentication.""" instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] - db_user = os.environ["DB_USER"] # Database username + db_user = os.environ["DB_USER"] # Database username db_name = os.environ["DB_NAME"] db_secret_name = os.environ["DB_SECRET_NAME"] ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") @@ -67,29 +82,62 @@ def get_password_connection() -> sqlalchemy.engine.base.Connection: ) return conn -# Create the SQLAlchemy engines -iam_engine = sqlalchemy.create_engine( - "mysql+pymysql://", - creator=get_iam_connection, -) -password_engine = sqlalchemy.create_engine( - "mysql+pymysql://", - creator=get_password_connection, -) + +# This example uses two distinct SQLAlchemy engines to demonstrate two different +# authentication methods (IAM and password-based) in the same application. +# +# In a typical production application, you would generally only need one +# SQLAlchemy engine, configured for your preferred authentication method. +# Both engines are defined globally to allow for connection pooling and +# reuse across requests. + + +def connect_with_password() -> sqlalchemy.engine.base.Connection: + """Initializes the connector and password engine if necessary, then returns a connection.""" + global connector, password_engine + + if connector is None: + connector = Connector(refresh_strategy="lazy") + + if password_engine is None: + password_engine = sqlalchemy.create_engine( + "mysql+pymysql://", + creator=get_password_connection, + ) + + return password_engine.connect() + + +def connect_with_iam() -> sqlalchemy.engine.base.Connection: + """Initializes the connector and IAM engine if necessary, then returns a connection.""" + global connector, iam_engine + + if connector is None: + connector = Connector(refresh_strategy="lazy") + + if iam_engine is None: + iam_engine = sqlalchemy.create_engine( + "mysql+pymysql://", + creator=get_iam_connection, + ) + + return iam_engine.connect() + @app.route("/") def password_auth_index(): try: - with password_engine.connect() as conn: + with connect_with_password() as conn: result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (password authentication), result: {result}" except Exception as e: return f"Error connecting to the database (password authentication)", 500 + @app.route("/iam") def iam_auth_index(): try: - with iam_engine.connect() as conn: + with connect_with_iam() as conn: result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (IAM authentication), result: {result}" except Exception as e: diff --git a/samples/cloudrun/cloudrun/mysql/requirements.txt b/samples/cloudrun/mysql/requirements.txt similarity index 100% rename from samples/cloudrun/cloudrun/mysql/requirements.txt rename to samples/cloudrun/mysql/requirements.txt diff --git a/samples/cloudrun/cloudrun/postgres/Dockerfile b/samples/cloudrun/postgres/Dockerfile similarity index 100% rename from samples/cloudrun/cloudrun/postgres/Dockerfile rename to samples/cloudrun/postgres/Dockerfile diff --git a/samples/cloudrun/cloudrun/postgres/main.py b/samples/cloudrun/postgres/main.py similarity index 53% rename from samples/cloudrun/cloudrun/postgres/main.py rename to samples/cloudrun/postgres/main.py index 7be0a185d..392a439a9 100644 --- a/samples/cloudrun/cloudrun/postgres/main.py +++ b/samples/cloudrun/postgres/main.py @@ -23,14 +23,27 @@ # Initialize Flask app app = Flask(__name__) -# Initialize the Connector object with lazy refresh -connector = Connector(refresh_strategy="lazy") +# Connector and SQLAlchemy engines are initialized as None to allow for lazy instantiation. +# +# The Connector object is a global variable to ensure that the same connector +# instance is used across all requests. This prevents the unnecessary creation +# of new Connector instances, which is inefficient and can lead to connection +# limits being reached. +# +# Lazy instantiation (initializing the Connector and Engine only when needed) +# allows the Cloud Run service to start up faster, as it avoids performing +# initialization tasks (like fetching secrets or metadata) during startup. +connector = None +iam_engine = None +password_engine = None secret_client = secretmanager.SecretManagerServiceClient() + # Function to create a database connection using IAM authentication def get_iam_connection() -> sqlalchemy.engine.base.Connection: + """Creates a database connection using IAM authentication.""" instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] - db_user = os.environ["DB_IAM_USER"] # IAM service account email + db_user = os.environ["DB_IAM_USER"] # IAM service account email db_name = os.environ["DB_NAME"] ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") ip_type = IPTypes[ip_type_str] @@ -41,14 +54,16 @@ def get_iam_connection() -> sqlalchemy.engine.base.Connection: user=db_user, db=db_name, ip_type=ip_type, - enable_iam_auth=True + enable_iam_auth=True, ) return conn + # Function to create a database connection using password-based authentication def get_password_connection() -> sqlalchemy.engine.base.Connection: + """Creates a database connection using password authentication.""" instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] - db_user = os.environ["DB_USER"] # Database username + db_user = os.environ["DB_USER"] # Database username db_name = os.environ["DB_NAME"] db_secret_name = os.environ["DB_SECRET_NAME"] ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") @@ -67,29 +82,62 @@ def get_password_connection() -> sqlalchemy.engine.base.Connection: ) return conn -# Create the SQLAlchemy engines -iam_engine = sqlalchemy.create_engine( - "postgresql+pg8000://", - creator=get_iam_connection, -) -password_engine = sqlalchemy.create_engine( - "postgresql+pg8000://", - creator=get_password_connection, -) + +# This example uses two distinct SQLAlchemy engines to demonstrate two different +# authentication methods (IAM and password-based) in the same application. +# +# In a typical production application, you would generally only need one +# SQLAlchemy engine, configured for your preferred authentication method. +# Both engines are defined globally to allow for connection pooling and +# reuse across requests. + + +def connect_with_password() -> sqlalchemy.engine.base.Connection: + """Initializes the connector and password engine if necessary, then returns a connection.""" + global connector, password_engine + + if connector is None: + connector = Connector(refresh_strategy="lazy") + + if password_engine is None: + password_engine = sqlalchemy.create_engine( + "postgresql+pg8000://", + creator=get_password_connection, + ) + + return password_engine.connect() + + +def connect_with_iam() -> sqlalchemy.engine.base.Connection: + """Initializes the connector and IAM engine if necessary, then returns a connection.""" + global connector, iam_engine + + if connector is None: + connector = Connector(refresh_strategy="lazy") + + if iam_engine is None: + iam_engine = sqlalchemy.create_engine( + "postgresql+pg8000://", + creator=get_iam_connection, + ) + + return iam_engine.connect() + @app.route("/") def password_auth_index(): try: - with password_engine.connect() as conn: + with connect_with_password() as conn: result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (password authentication), result: {result}" except Exception as e: return f"Error connecting to the database (password authentication)", 500 + @app.route("/iam") def iam_auth_index(): try: - with iam_engine.connect() as conn: + with connect_with_iam() as conn: result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful (IAM authentication), result: {result}" except Exception as e: diff --git a/samples/cloudrun/cloudrun/postgres/requirements.txt b/samples/cloudrun/postgres/requirements.txt similarity index 100% rename from samples/cloudrun/cloudrun/postgres/requirements.txt rename to samples/cloudrun/postgres/requirements.txt diff --git a/samples/cloudrun/cloudrun/sqlserver/Dockerfile b/samples/cloudrun/sqlserver/Dockerfile similarity index 100% rename from samples/cloudrun/cloudrun/sqlserver/Dockerfile rename to samples/cloudrun/sqlserver/Dockerfile diff --git a/samples/cloudrun/cloudrun/sqlserver/main.py b/samples/cloudrun/sqlserver/main.py similarity index 65% rename from samples/cloudrun/cloudrun/sqlserver/main.py rename to samples/cloudrun/sqlserver/main.py index f2f638174..721ebf286 100644 --- a/samples/cloudrun/cloudrun/sqlserver/main.py +++ b/samples/cloudrun/sqlserver/main.py @@ -23,8 +23,19 @@ # Initialize Flask app app = Flask(__name__) -# Initialize the Connector object with lazy refresh -connector = Connector(refresh_strategy="lazy") +# Connector and SQLAlchemy engine are initialized as None to allow for lazy instantiation. +# +# The Connector object is a global variable to ensure that the same connector +# instance is used across all requests. This prevents the unnecessary creation +# of new Connector instances, which is inefficient and can lead to connection +# limits being reached. +# +# Lazy instantiation (initializing the Connector and Engine only when needed) +# allows the Cloud Run service to start up faster, as it avoids performing +# initialization tasks (like fetching secrets or metadata) during startup. +connector = None +engine = None + def get_connection() -> sqlalchemy.engine.base.Connection: """ @@ -54,16 +65,27 @@ def get_connection() -> sqlalchemy.engine.base.Connection: ) return conn -# Create the SQLAlchemy engine -engine = sqlalchemy.create_engine( - "mssql+pytds://", - creator=get_connection, -) + +def connect_to_db() -> sqlalchemy.engine.base.Connection: + """Initializes the connector and engine if necessary, then returns a connection.""" + global connector, engine + + if connector is None: + connector = Connector(refresh_strategy="lazy") + + if engine is None: + engine = sqlalchemy.create_engine( + "mssql+pytds://", + creator=get_connection, + ) + + return engine.connect() + @app.route("/") def index(): try: - with engine.connect() as conn: + with connect_to_db() as conn: result = conn.execute(sqlalchemy.text("SELECT 1")).fetchall() return f"Database connection successful, result: {result}" except Exception as e: diff --git a/samples/cloudrun/cloudrun/sqlserver/requirements.txt b/samples/cloudrun/sqlserver/requirements.txt similarity index 100% rename from samples/cloudrun/cloudrun/sqlserver/requirements.txt rename to samples/cloudrun/sqlserver/requirements.txt From 7c9de11820d098e0bbdf175e74730aafa174788b Mon Sep 17 00:00:00 2001 From: kgala2 Date: Thu, 20 Nov 2025 21:54:00 +0000 Subject: [PATCH 4/5] chore: update db password setup --- samples/cloudrun/README.md | 24 +++++++++++++++++++----- samples/cloudrun/mysql/main.py | 9 ++------- samples/cloudrun/postgres/main.py | 6 +----- samples/cloudrun/sqlserver/main.py | 8 +------- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/samples/cloudrun/README.md b/samples/cloudrun/README.md index c5d8e6279..2e5bff2c1 100644 --- a/samples/cloudrun/README.md +++ b/samples/cloudrun/README.md @@ -60,6 +60,16 @@ For IAM authentication to work, you must ensure two things: ``` Replace `SERVICE_ACCOUNT_EMAIL` with the same service account email and `INSTANCE_NAME` with your Cloud SQL instance name. +For Password-based authentication to work: + +1. **The Cloud Run service's service account has the `Secret Accessor` role.** You can grant this role with the following command: + ```bash + gcloud projects add-iam-policy-binding PROJECT_ID \ + --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \ + --role="roles/secretmanager.secretAccessor" + ``` + Replace `PROJECT_ID` with your Google Cloud project ID and `SERVICE_ACCOUNT_EMAIL` with the email of the service account your Cloud Run service is using. + ## Deploy the Application to Cloud Run Follow these steps to deploy the application to Cloud Run. @@ -113,7 +123,7 @@ Deploy the container image to Cloud Run using the `gcloud run deploy` command. * `DB_USER`: `my-db-user` (for password-based authentication) * `DB_IAM_USER`: `my-service-account@my-gcp-project-id.iam.gserviceaccount.com` (for IAM-based authentication) * `DB_NAME`: `my-db-name` -* `DB_SECRET_NAME`: `projects/my-gcp-project-id/secrets/my-db-secret/versions/latest` +* `DB_PASSWORD`: `my-user-pass-secret-name` * `VPC_NETWORK`: `my-vpc-network` * `SUBNET_NAME`: `my-vpc-subnet` @@ -125,7 +135,8 @@ gcloud run deploy SERVICE_NAME \ --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ --set-env-vars=DB_USER=DB_USER,DB_IAM_USER=DB_IAM_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \ - --region=REGION + --region=REGION \ + --update-secrets=DB_PASSWORD=DB_PASSWORD:latest ``` **For MySQL and PostgreSQL (Private IP):** @@ -138,7 +149,8 @@ gcloud run deploy SERVICE_NAME \ --network=VPC_NETWORK \ --subnet=SUBNET_NAME \ --vpc-egress=private-ranges-only \ - --region=REGION + --region=REGION \ + --update-secrets=DB_PASSWORD=DB_PASSWORD:latest ``` **For SQL Server (Public IP):** @@ -148,7 +160,8 @@ gcloud run deploy SERVICE_NAME \ --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ --set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \ - --region=REGION + --region=REGION \ + --update-secrets=DB_PASSWORD=DB_PASSWORD:latest ``` **For SQL Server (Private IP):** @@ -161,7 +174,8 @@ gcloud run deploy SERVICE_NAME \ --network=VPC_NETWORK \ --subnet=SUBNET_NAME \ --vpc-egress=private-ranges-only \ - --region=REGION + --region=REGION \ + --update-secrets=DB_PASSWORD=DB_PASSWORD:latest ``` > [!NOTE] diff --git a/samples/cloudrun/mysql/main.py b/samples/cloudrun/mysql/main.py index ce0fdfd94..b1b546682 100644 --- a/samples/cloudrun/mysql/main.py +++ b/samples/cloudrun/mysql/main.py @@ -18,7 +18,6 @@ import sqlalchemy from flask import Flask from google.cloud.sql.connector import Connector, IPTypes -from google.cloud import secretmanager # Initialize Flask app app = Flask(__name__) @@ -36,7 +35,6 @@ connector = None iam_engine = None password_engine = None -secret_client = secretmanager.SecretManagerServiceClient() # Function to create a database connection using IAM authentication @@ -65,12 +63,9 @@ def get_password_connection() -> sqlalchemy.engine.base.Connection: instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] db_user = os.environ["DB_USER"] # Database username db_name = os.environ["DB_NAME"] - db_secret_name = os.environ["DB_SECRET_NAME"] + db_password = os.environ["DB_PASSWORD"] ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") - ip_type = IPTypes[ip_type_str] - - secret_response = secret_client.access_secret_version(name=db_secret_name) - db_password = secret_response.payload.data.decode("UTF-8") + ip_type = IPTypes[ip_type_str] conn = connector.connect( instance_connection_name, diff --git a/samples/cloudrun/postgres/main.py b/samples/cloudrun/postgres/main.py index 392a439a9..e33d5a06c 100644 --- a/samples/cloudrun/postgres/main.py +++ b/samples/cloudrun/postgres/main.py @@ -18,7 +18,6 @@ import sqlalchemy from flask import Flask from google.cloud.sql.connector import Connector, IPTypes -from google.cloud import secretmanager # Initialize Flask app app = Flask(__name__) @@ -36,7 +35,6 @@ connector = None iam_engine = None password_engine = None -secret_client = secretmanager.SecretManagerServiceClient() # Function to create a database connection using IAM authentication @@ -65,12 +63,10 @@ def get_password_connection() -> sqlalchemy.engine.base.Connection: instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] db_user = os.environ["DB_USER"] # Database username db_name = os.environ["DB_NAME"] - db_secret_name = os.environ["DB_SECRET_NAME"] + db_password = os.environ["DB_PASSWORD"] ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") ip_type = IPTypes[ip_type_str] - secret_response = secret_client.access_secret_version(name=db_secret_name) - db_password = secret_response.payload.data.decode("UTF-8") conn = connector.connect( instance_connection_name, diff --git a/samples/cloudrun/sqlserver/main.py b/samples/cloudrun/sqlserver/main.py index 721ebf286..0ce8162cf 100644 --- a/samples/cloudrun/sqlserver/main.py +++ b/samples/cloudrun/sqlserver/main.py @@ -18,7 +18,6 @@ import sqlalchemy from flask import Flask from google.cloud.sql.connector import Connector, IPTypes -from google.cloud import secretmanager # Initialize Flask app app = Flask(__name__) @@ -45,15 +44,10 @@ def get_connection() -> sqlalchemy.engine.base.Connection: instance_connection_name = os.environ["INSTANCE_CONNECTION_NAME"] db_user = os.environ["DB_USER"] db_name = os.environ["DB_NAME"] - db_secret_name = os.environ["DB_SECRET_NAME"] + db_password = os.environ["DB_PASSWORD"] ip_type_str = os.environ.get("IP_TYPE", "PUBLIC") ip_type = IPTypes[ip_type_str] - # Get the database password from Secret Manager - secret_client = secretmanager.SecretManagerServiceClient() - secret_response = secret_client.access_secret_version(name=db_secret_name) - db_password = secret_response.payload.data.decode("UTF-8") - # Connect to the database conn = connector.connect( instance_connection_name, From b4f88632a7a1144cf3185ac64de8c885360e36b5 Mon Sep 17 00:00:00 2001 From: kgala2 Date: Mon, 24 Nov 2025 21:23:29 +0000 Subject: [PATCH 5/5] chore: remove add-cloudsql-instances flag --- samples/cloudrun/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/samples/cloudrun/README.md b/samples/cloudrun/README.md index 2e5bff2c1..2c2b7eb72 100644 --- a/samples/cloudrun/README.md +++ b/samples/cloudrun/README.md @@ -133,7 +133,6 @@ Deploy the container image to Cloud Run using the `gcloud run deploy` command. ```bash gcloud run deploy SERVICE_NAME \ --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ - --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ --set-env-vars=DB_USER=DB_USER,DB_IAM_USER=DB_IAM_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \ --region=REGION \ --update-secrets=DB_PASSWORD=DB_PASSWORD:latest @@ -144,7 +143,6 @@ gcloud run deploy SERVICE_NAME \ ```bash gcloud run deploy SERVICE_NAME \ --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ - --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ --set-env-vars=DB_USER=DB_USER,DB_IAM_USER=DB_IAM_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME,IP_TYPE=PRIVATE \ --network=VPC_NETWORK \ --subnet=SUBNET_NAME \ @@ -158,7 +156,6 @@ gcloud run deploy SERVICE_NAME \ ```bash gcloud run deploy SERVICE_NAME \ --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \ - --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ --set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \ --region=REGION \ --update-secrets=DB_PASSWORD=DB_PASSWORD:latest @@ -169,7 +166,6 @@ gcloud run deploy SERVICE_NAME \ ```bash gcloud run deploy SERVICE_NAME \ --image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_name \ - --add-cloudsql-instances=INSTANCE_CONNECTION_NAME \ --set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME,IP_TYPE=PRIVATE \ --network=VPC_NETWORK \ --subnet=SUBNET_NAME \