[Python] Cloud Function -> Python connect to PostgreSQL via IAM user

Discussão em 'Python' iniciado por Stack, Agosto 25, 2025.

  1. Stack

    Stack Membro Participativo

    So im only just starting with GCP/Python.. and am still in the learning phase..

    Im using Terraform/Github to deploy my packages to GCP.

    Im trying to get a pub/sub to trigger a Cloud Run Function to run a Python Script to connect to a PostgreSQL DB and do a simple count on a table and print it to the log. But it looks like it doesnt want to connect.

    Ive a few TF files to keep things a bit tidy.. but put them all in the block below to show together

    I dont want to go down the Private IP via connector.. its an extra $36 a month which i dont want to pay for something im just playing around with..

    resource "google_cloudfunctions2_function" "monster_count_fn" {
    name = var.function_name
    project = var.project # <— was var.project_id
    location = var.region

    build_config {
    runtime = "python311"
    entry_point = "count_tmonsters"
    source {
    storage_source {
    bucket = var.code_bucket
    object = var.code_object
    }
    }
    }

    service_config {
    available_memory = "256M"
    timeout_seconds = 60
    max_instance_count = 1
    service_account_email = google_service_account.game_engine.email
    environment_variables = {
    INSTANCE_CONNECTION_NAME = var.instance_connection_name
    DB_USER = var.db_user
    DB_NAME = var.db_name
    }
    ingress_settings = "ALLOW_ALL"
    }

    event_trigger {
    trigger_region = var.region
    event_type = "google.cloud.pubsub.topic.v1.messagePublished"
    pubsub_topic = google_pubsub_topic.monster_count.id
    retry_policy = "RETRY_POLICY_RETRY"
    }

    # Make this function wait for your API enables
    depends_on = [
    google_project_service.cloudresourcemanager,
    google_project_service.project_apis
    ]
    }

    variable "db_user" {
    type = string
    default = "game-engine@rpg-dev.iam"
    }

    # Ensure the necessary APIs are enabled for the game_engine service account.
    locals {
    game_engine_roles = {
    "roles/cloudsql.client" = "sqladmin.googleapis.com"
    "roles/cloudsql.instanceUser" = "sqladmin.googleapis.com"
    "roles/logging.logWriter" = "logging.googleapis.com"
    "roles/eventarc.eventReceiver" = "eventarc.googleapis.com" # Required for Pub/Sub triggered Gen 2 functions
    # Add more if needed later (e.g. storage, secrets)
    }
    }

    # Grant the game engine service account the necessary roles for its operations.
    # These roles are required for the game engine to interact with Cloud SQL, Pub/Sub, and other services.
    resource "google_project_iam_member" "game_engine_access" {
    for_each = local.game_engine_roles

    project = var.project
    role = each.key
    member = "serviceAccount:${google_service_account.game_engine.email}"

    depends_on = [google_project_service.project_apis]
    }


    And Below is the Python Script im trying to run via the cloud function:

    import base64
    import json
    import logging, sys, os
    from google.cloud.sql.connector import Connector, IPTypes
    import pg8000

    logging.basicConfig(
    level=logging.DEBUG,
    format="%(levelname)s %(asctime)s %(filename)s:%(lineno)d - %(message)s",
    stream=sys.stdout,
    force=True,
    )

    connector = Connector()

    def _get_conn():
    inst = os.environ["INSTANCE_CONNECTION_NAME"]
    db = os.environ["DB_NAME"]
    user = os.environ["DB_USER"]
    logging.debug("Using instance=%s user=%s db=%s", inst, user, db)
    # Public IP path; IAM DB Auth
    return connector.connect(
    inst,
    "pg8000",
    user=user,
    db=db,
    enable_iam_auth=True,
    ip_type=IPTypes.PUBLIC,
    )

    def _decode_event(event):
    try:
    if isinstance(event, dict) and "data" in event:
    return base64.b64decode(event["data"]).decode("utf-8", "ignore")
    except Exception as e:
    logging.warning("event decode failed: %s", e, exc_info=True)
    return None

    def count_tmonsters(event, context):
    logging.debug("Handler start. eventId=%s, event=%s", getattr(context, "event_id", None), event)

    # Helpful runtime identity / env diagnostics
    logging.debug("ENV PROJECT_ID=%s, SERVICE_ACCOUNT=%s",
    os.getenv("GCP_PROJECT") or os.getenv("GOOGLE_CLOUD_PROJECT"),
    os.getenv("K_SERVICE_ACCOUNT") or os.getenv("GOOGLE_SERVICE_ACCOUNT") or "unknown")
    logging.debug("INSTANCE_CONNECTION_NAME=%s, DB_USER=%s, DB_NAME=%s",
    os.getenv("INSTANCE_CONNECTION_NAME"), os.getenv("DB_USER"), os.getenv("DB_NAME"))

    msg = _decode_event(event)
    if msg:
    logging.info("Pub/Sub message: %s", msg)

    conn = None
    cur = None
    try:
    logging.debug("Attempting DB connection with IAM auth...")
    conn = _get_conn()
    logging.info("DB connection established")
    cur = conn.cursor()
    logging.debug("Running query…")
    cur.execute("SELECT COUNT(*) FROM tmonsters;")
    row = cur.fetchone()
    logging.info("monster_count=%s", row[0] if row else None)
    except Exception as e:
    logging.error("DB operation failed: %s", e, exc_info=True)
    # Surface a short line as well (some platforms trim long traces)
    logging.error("FAIL_REASON: %r", e)
    finally:
    try:
    if cur:
    cur.close()
    except Exception as e:
    logging.warning("cursor close error: %s", e, exc_info=True)
    try:
    if conn:
    conn.close()
    except Exception as e:
    logging.warning("conn close error: %s", e, exc_info=True)

    logging.debug("Handler end")



    And when i check the log, all it shows is:

    monster-count-fn 2025-08-25 11:57:47.324 DEBUG 2025-08-25 11:57:47,325 main.py:25 - Using instance=cso-rpg-dev:europe-west2:cso-rpg-sql user=game-engine@cso-rpg-dev.iam db=rpg
    monster-count-fn 2025-08-25 11:57:47.323 DEBUG 2025-08-25 11:57:47,325 main.py:19 - Attempting DB connection with IAM auth...
    monster-count-fn 2025-08-25 11:57:47.323 DEBUG 2025-08-25 11:57:47,324 main.py:45 - Handler start. eventId=15973659715120867, event={'data': 'dGVzdA==', 'message_id': '15973659715120867', 'publish_time': '2025-08-25T11:19:58.156Z'}
    E monster-count-fn 2025-08-25 11:57:47.304
    monster-count-fn 2025-08-25 11:56:40.205 DEBUG 2025-08-25 11:56:40,206 main.py:25 - Using instance=cso-rpg-dev:europe-west2:cso-rpg-sql user=game-engine@cso-rpg-dev.iam db=rpg
    monster-count-fn 2025-08-25 11:56:40.205 DEBUG 2025-08-25 11:56:40,206 main.py:19 - Attempting DB connection with IAM auth...
    monster-count-fn 2025-08-25 11:56:40.205 DEBUG 2025-08-25 11:56:40,206 main.py:45 - Handler start. eventId=15975546303899728, event={'data': 'dGVzdA==', 'message_id': '15975546303899728', 'publish_time': '2025-08-25T11:55:50.993Z'}
    E monster-count-fn 2025-08-25 11:56:38.272
    monster-count-fn 2025-08-25 11:55:40.211 DEBUG 2025-08-25 11:55:40,212 main.py:25 - Using instance=cso-rpg-dev:europe-west2:cso-rpg-sql user=game-engine@cso-rpg-dev.iam db=rpg
    monster-count-fn 2025-08-25 11:55:40.211 DEBUG 2025-08-25 11:55:40,212 main.py:19 - Attempting DB connection with IAM auth...
    monster-count-fn 2025-08-25 11:55:40.211 DEBUG 2025-08-25 11:55:40,212 main.py:45 - Handler start. eventId=15973204489076835, event={'data': 'dGVzdA==', 'message_id': '15973204489076835', 'publish_time': '2025-08-25T11:22:56.173Z'}
    E monster-count-fn 2025-08-25 11:55:40.175
    I monster-count-fn 2025-08-25 11:52:32.048 Default STARTUP TCP probe succeeded after 1 attempt for container "worker" on port 8080.
    monster-count-fn 2025-08-25 11:52:31.933 DEBUG 2025-08-25 11:52:31,934 connectionpool.py:544 - http://metadata.google.internal:80 "GET /computeMetadata/v1/universe/universe-domain HTTP/1.1" 200 14
    monster-count-fn 2025-08-25 11:52:31.932 DEBUG 2025-08-25 11:52:31,932 connectionpool.py:241 - Starting new HTTP connection (1): metadata.google.internal:80
    monster-count-fn 2025-08-25 11:52:31.883 DEBUG 2025-08-25 11:52:31,884 _default.py:256 - Cloud SDK credentials not found on disk; not using them
    monster-count-fn 2025-08-25 11:52:31.883 DEBUG 2025-08-25 11:52:31,884 _default.py:250 - Checking Cloud SDK credentials as part of auth process...
    monster-count-fn 2025-08-25 11:52:31.883 DEBUG 2025-08-25 11:52:31,883 _default.py:278 - Checking None for explicit credentials as part of auth process...
    monster-count-fn 2025-08-25 11:52:31.797 DEBUG 2025-08-25 11:52:31,797 selector_events.py:54 - Using selector: EpollSelector
    I monster-count-fn 2025-08-25 11:52:28.304 Starting new instance. Reason: DEPLOYMENT_ROLLOUT - Instance started due to traffic shifting between revisions due to deployment, traffic split adjustment, or deployment health check.

    Continue reading...

Compartilhe esta Página