|
14 | 14 | limitations under the License. |
15 | 15 | """ |
16 | 16 |
|
| 17 | +import asyncio |
17 | 18 | from datetime import datetime |
18 | 19 | import os |
19 | 20 |
|
20 | 21 | # [START cloud_sql_connector_postgres_psycopg] |
21 | 22 | from typing import Union |
22 | 23 |
|
23 | 24 | from psycopg import Connection |
| 25 | +import pytest |
| 26 | +import logging |
24 | 27 | import sqlalchemy |
25 | 28 |
|
26 | 29 | from google.cloud.sql.connector import Connector |
|
29 | 32 |
|
30 | 33 | SERVER_PROXY_PORT = 3307 |
31 | 34 |
|
32 | | -async def create_sqlalchemy_engine( |
33 | | - instance_connection_name: str, |
34 | | - user: str, |
35 | | - password: str, |
36 | | - db: str, |
37 | | - ip_type: str = "public", |
38 | | - refresh_strategy: str = "background", |
39 | | - resolver: Union[type[DefaultResolver], type[DnsResolver]] = DefaultResolver, |
40 | | -) -> tuple[sqlalchemy.engine.Engine, Connector]: |
41 | | - """Creates a connection pool for a Cloud SQL instance and returns the pool |
42 | | - and the connector. Callers are responsible for closing the pool and the |
43 | | - connector. |
44 | | -
|
45 | | - A sample invocation looks like: |
46 | | -
|
47 | | - engine, connector = create_sqlalchemy_engine( |
48 | | - inst_conn_name, |
49 | | - user, |
50 | | - password, |
51 | | - db, |
52 | | - ) |
53 | | - with engine.connect() as conn: |
54 | | - time = conn.execute(sqlalchemy.text("SELECT NOW()")).fetchone() |
55 | | - conn.commit() |
56 | | - curr_time = time[0] |
57 | | - # do something with query result |
58 | | - connector.close() |
59 | | -
|
60 | | - Args: |
61 | | - instance_connection_name (str): |
62 | | - The instance connection name specifies the instance relative to the |
63 | | - project and region. For example: "my-project:my-region:my-instance" |
64 | | - user (str): |
65 | | - The database user name, e.g., root |
66 | | - password (str): |
67 | | - The database user's password, e.g., secret-password |
68 | | - db (str): |
69 | | - The name of the database, e.g., mydb |
70 | | - ip_type (str): |
71 | | - The IP type of the Cloud SQL instance to connect to. Can be one |
72 | | - of "public", "private", or "psc". |
73 | | - refresh_strategy (Optional[str]): |
74 | | - Refresh strategy for the Cloud SQL Connector. Can be one of "lazy" |
75 | | - or "background". For serverless environments use "lazy" to avoid |
76 | | - errors resulting from CPU being throttled. |
77 | | - resolver (Optional[google.cloud.sql.connector.DefaultResolver]): |
78 | | - Resolver class for resolving instance connection name. Use |
79 | | - google.cloud.sql.connector.DnsResolver when resolving DNS domain |
80 | | - names or google.cloud.sql.connector.DefaultResolver for regular |
81 | | - instance connection names ("my-project:my-region:my-instance"). |
82 | | - """ |
83 | | - connector = Connector(refresh_strategy=refresh_strategy, resolver=resolver) |
84 | | - unix_socket_folder = "/tmp/conn" |
85 | | - unix_socket_path = f"{unix_socket_folder}/.s.PGSQL.3307" |
86 | | - await connector.start_unix_socket_proxy_async( |
87 | | - instance_connection_name, |
88 | | - unix_socket_path, |
89 | | - ip_type=ip_type, # can be "public", "private" or "psc" |
90 | | - ) |
91 | | - |
92 | | - # create SQLAlchemy connection pool |
93 | | - engine = sqlalchemy.create_engine( |
94 | | - "postgresql+psycopg://", |
95 | | - creator=lambda: Connection.connect( |
96 | | - f"host={unix_socket_folder} port={SERVER_PROXY_PORT} dbname={db} user={user} password={password} sslmode=require", |
97 | | - user=user, |
98 | | - password=password, |
99 | | - dbname=db, |
100 | | - autocommit=True, |
101 | | - ) |
102 | | - ) |
103 | | - |
104 | | - return engine, connector |
105 | | - |
| 35 | +logger = logging.getLogger(name=__name__) |
106 | 36 |
|
107 | 37 | # [END cloud_sql_connector_postgres_psycopg] |
108 | 38 |
|
109 | 39 |
|
| 40 | +@pytest.mark.asyncio |
110 | 41 | async def test_psycopg_connection() -> None: |
111 | 42 | """Basic test to get time from database.""" |
112 | | - inst_conn_name = os.environ["POSTGRES_CONNECTION_NAME"] |
| 43 | + instance_connection_name = os.environ["POSTGRES_CONNECTION_NAME"] |
113 | 44 | user = os.environ["POSTGRES_USER"] |
114 | 45 | password = os.environ["POSTGRES_PASS"] |
115 | 46 | db = os.environ["POSTGRES_DB"] |
116 | 47 | ip_type = os.environ.get("IP_TYPE", "public") |
117 | 48 |
|
118 | | - engine, connector = await create_sqlalchemy_engine( |
119 | | - inst_conn_name, user, password, db, ip_type |
120 | | - ) |
121 | | - with engine.connect() as conn: |
122 | | - time = conn.execute(sqlalchemy.text("SELECT NOW()")).fetchone() |
123 | | - conn.commit() |
124 | | - curr_time = time[0] |
125 | | - assert type(curr_time) is datetime |
126 | | - connector.close() |
| 49 | + unix_socket_folder = "/tmp/conn" |
| 50 | + unix_socket_path = f"{unix_socket_folder}/.s.PGSQL.3307" |
| 51 | + |
| 52 | + async with Connector( |
| 53 | + refresh_strategy='lazy', resolver=DefaultResolver |
| 54 | + ) as connector: |
| 55 | + # Open proxy connection |
| 56 | + # start the proxy server |
| 57 | + |
| 58 | + await connector.start_unix_socket_proxy_async( |
| 59 | + instance_connection_name, |
| 60 | + unix_socket_path, |
| 61 | + driver="psycopg", |
| 62 | + user=user, |
| 63 | + password=password, |
| 64 | + db=db, |
| 65 | + ip_type=ip_type, # can be "public", "private" or "psc" |
| 66 | + ) |
| 67 | + |
| 68 | + # Wait for server to start |
| 69 | + await asyncio.sleep(0.5) |
| 70 | + |
| 71 | + engine = sqlalchemy.create_engine( |
| 72 | + "postgresql+psycopg://", |
| 73 | + creator=lambda: Connection.connect( |
| 74 | + f"host={unix_socket_folder} port={SERVER_PROXY_PORT} dbname={db} user={user} password={password} sslmode=require", |
| 75 | + user=user, |
| 76 | + password=password, |
| 77 | + dbname=db, |
| 78 | + autocommit=True, |
| 79 | + ) |
| 80 | + ) |
| 81 | + |
| 82 | + with engine.connect() as conn: |
| 83 | + time = conn.execute(sqlalchemy.text("SELECT NOW()")).fetchone() |
| 84 | + conn.commit() |
| 85 | + curr_time = time[0] |
| 86 | + assert type(curr_time) is datetime |
| 87 | + connector.close() |
0 commit comments