cloud-sql-python-connector 1.17.0.dev0__py3-none-any.whl → 1.18.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,15 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: cloud-sql-python-connector
3
- Version: 1.17.0.dev0
3
+ Version: 1.18.1
4
4
  Summary: Google Cloud SQL Python Connector library
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
- License: Apache 2.0
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector
8
8
  Project-URL: Repository, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector.git
9
9
  Project-URL: Issues, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/issues
10
10
  Project-URL: Changelog, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/blob/main/CHANGELOG.md
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: Apache Software License
14
13
  Classifier: Programming Language :: Python
15
14
  Classifier: Programming Language :: Python :: 3
16
15
  Classifier: Programming Language :: Python :: 3.9
@@ -36,6 +35,7 @@ Provides-Extra: pytds
36
35
  Requires-Dist: python-tds>=1.15.0; extra == "pytds"
37
36
  Provides-Extra: asyncpg
38
37
  Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
38
+ Dynamic: license-file
39
39
 
40
40
  <p align="center">
41
41
  <a href="https://cloud.google.com/blog/topics/developers-practitioners/how-connect-cloud-sql-using-python-easy-way">
@@ -165,21 +165,16 @@ import sqlalchemy
165
165
  # initialize Connector object
166
166
  connector = Connector()
167
167
 
168
- # function to return the database connection
169
- def getconn() -> pymysql.connections.Connection:
170
- conn: pymysql.connections.Connection = connector.connect(
168
+ # initialize SQLAlchemy connection pool with Connector
169
+ pool = sqlalchemy.create_engine(
170
+ "mysql+pymysql://",
171
+ creator=lambda: connector.connect(
171
172
  "project:region:instance",
172
173
  "pymysql",
173
174
  user="my-user",
174
175
  password="my-password",
175
176
  db="my-db-name"
176
- )
177
- return conn
178
-
179
- # create connection pool
180
- pool = sqlalchemy.create_engine(
181
- "mysql+pymysql://",
182
- creator=getconn,
177
+ ),
183
178
  )
184
179
  ```
185
180
 
@@ -246,33 +241,21 @@ Connector as a context manager:
246
241
 
247
242
  ```python
248
243
  from google.cloud.sql.connector import Connector
249
- import pymysql
250
244
  import sqlalchemy
251
245
 
252
- # helper function to return SQLAlchemy connection pool
253
- def init_connection_pool(connector: Connector) -> sqlalchemy.engine.Engine:
254
- # function used to generate database connection
255
- def getconn() -> pymysql.connections.Connection:
256
- conn = connector.connect(
246
+ # initialize Cloud SQL Python Connector as context manager
247
+ with Connector() as connector:
248
+ # initialize SQLAlchemy connection pool with Connector
249
+ pool = sqlalchemy.create_engine(
250
+ "mysql+pymysql://",
251
+ creator=lambda: connector.connect(
257
252
  "project:region:instance",
258
253
  "pymysql",
259
254
  user="my-user",
260
255
  password="my-password",
261
256
  db="my-db-name"
262
- )
263
- return conn
264
-
265
- # create connection pool
266
- pool = sqlalchemy.create_engine(
267
- "mysql+pymysql://",
268
- creator=getconn,
257
+ ),
269
258
  )
270
- return pool
271
-
272
- # initialize Cloud SQL Python Connector as context manager
273
- with Connector() as connector:
274
- # initialize connection pool
275
- pool = init_connection_pool(connector)
276
259
  # insert statement
277
260
  insert_stmt = sqlalchemy.text(
278
261
  "INSERT INTO my_table (id, title) VALUES (:id, :title)",
@@ -440,33 +423,60 @@ from google.cloud.sql.connector import Connector, DnsResolver
440
423
  import pymysql
441
424
  import sqlalchemy
442
425
 
443
- # helper function to return SQLAlchemy connection pool
444
- def init_connection_pool(connector: Connector) -> sqlalchemy.engine.Engine:
445
- # function used to generate database connection
446
- def getconn() -> pymysql.connections.Connection:
447
- conn = connector.connect(
426
+ # initialize Cloud SQL Python Connector with `resolver=DnsResolver`
427
+ with Connector(resolver=DnsResolver) as connector:
428
+ # initialize SQLAlchemy connection pool with Connector
429
+ pool = sqlalchemy.create_engine(
430
+ "mysql+pymysql://",
431
+ creator=lambda: connector.connect(
448
432
  "prod-db.mycompany.example.com", # using DNS name
449
433
  "pymysql",
450
434
  user="my-user",
451
435
  password="my-password",
452
436
  db="my-db-name"
453
- )
454
- return conn
455
-
456
- # create connection pool
457
- pool = sqlalchemy.create_engine(
458
- "mysql+pymysql://",
459
- creator=getconn,
437
+ ),
460
438
  )
461
- return pool
462
-
463
- # initialize Cloud SQL Python Connector with `resolver=DnsResolver`
464
- with Connector(resolver=DnsResolver) as connector:
465
- # initialize connection pool
466
- pool = init_connection_pool(connector)
467
439
  # ... use SQLAlchemy engine normally
468
440
  ```
469
441
 
442
+ ### Automatic failover using DNS domain names
443
+
444
+ > [!NOTE]
445
+ >
446
+ > Usage of the `asyncpg` driver does not currently support automatic failover.
447
+
448
+ When the connector is configured using a domain name, the connector will
449
+ periodically check if the DNS record for an instance changes. When the connector
450
+ detects that the domain name refers to a different instance, the connector will
451
+ close all open connections to the old instance. Subsequent connection attempts
452
+ will be directed to the new instance.
453
+
454
+ For example: suppose application is configured to connect using the
455
+ domain name `prod-db.mycompany.example.com`. Initially the private DNS
456
+ zone has a TXT record with the value `my-project:region:my-instance`. The
457
+ application establishes connections to the `my-project:region:my-instance`
458
+ Cloud SQL instance.
459
+
460
+ Then, to reconfigure the application to use a different database
461
+ instance, change the value of the `prod-db.mycompany.example.com` DNS record
462
+ from `my-project:region:my-instance` to `my-project:other-region:my-instance-2`
463
+
464
+ The connector inside the application detects the change to this
465
+ DNS record. Now, when the application connects to its database using the
466
+ domain name `prod-db.mycompany.example.com`, it will connect to the
467
+ `my-project:other-region:my-instance-2` Cloud SQL instance.
468
+
469
+ The connector will automatically close all existing connections to
470
+ `my-project:region:my-instance`. This will force the connection pools to
471
+ establish new connections. Also, it may cause database queries in progress
472
+ to fail.
473
+
474
+ The connector will poll for changes to the DNS name every 30 seconds by default.
475
+ You may configure the frequency of the connections using the Connector's
476
+ `failover_period` argument (i.e. `Connector(failover_period=60`). When this is
477
+ set to 0, the connector will disable polling and only check if the DNS record
478
+ changed when it is creating a new connection.
479
+
470
480
  ### Using the Python Connector with Python Web Frameworks
471
481
 
472
482
  The Python Connector can be used alongside popular Python web frameworks such
@@ -502,9 +512,12 @@ from google.cloud.sql.connector import Connector
502
512
  # initialize Python Connector object
503
513
  connector = Connector()
504
514
 
505
- # Python Connector database connection function
506
- def getconn():
507
- conn = connector.connect(
515
+ app = Flask(__name__)
516
+
517
+ # configure Flask-SQLAlchemy to use Python Connector
518
+ app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql+pg8000://"
519
+ app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
520
+ "creator": lambda: conn = connector.connect(
508
521
  "project:region:instance-name", # Cloud SQL Instance Connection Name
509
522
  "pg8000",
510
523
  user="my-user",
@@ -512,15 +525,6 @@ def getconn():
512
525
  db="my-database",
513
526
  ip_type="public" # "private" for private IP
514
527
  )
515
- return conn
516
-
517
-
518
- app = Flask(__name__)
519
-
520
- # configure Flask-SQLAlchemy to use Python Connector
521
- app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql+pg8000://"
522
- app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
523
- "creator": getconn
524
528
  }
525
529
 
526
530
  # initialize the app with the extension
@@ -541,38 +545,27 @@ your web application using [SQLAlchemy ORM](https://docs.sqlalchemy.org/en/14/or
541
545
  through the following:
542
546
 
543
547
  ```python
544
- from sqlalchemy import create_engine
545
- from sqlalchemy.engine import Engine
548
+ import sqlalchemy
546
549
  from sqlalchemy.ext.declarative import declarative_base
547
550
  from sqlalchemy.orm import sessionmaker
548
551
  from google.cloud.sql.connector import Connector
549
552
 
550
- # helper function to return SQLAlchemy connection pool
551
- def init_connection_pool(connector: Connector) -> Engine:
552
- # Python Connector database connection function
553
- def getconn():
554
- conn = connector.connect(
555
- "project:region:instance-name", # Cloud SQL Instance Connection Name
556
- "pg8000",
557
- user="my-user",
558
- password="my-password",
559
- db="my-database",
560
- ip_type="public" # "private" for private IP
561
- )
562
- return conn
563
-
564
- SQLALCHEMY_DATABASE_URL = "postgresql+pg8000://"
565
-
566
- engine = create_engine(
567
- SQLALCHEMY_DATABASE_URL , creator=getconn
568
- )
569
- return engine
570
553
 
571
554
  # initialize Cloud SQL Python Connector
572
555
  connector = Connector()
573
556
 
574
557
  # create connection pool engine
575
- engine = init_connection_pool(connector)
558
+ engine = sqlalchemy.create_engine(
559
+ "postgresql+pg8000://",
560
+ creator=lambda: connector.connect(
561
+ "project:region:instance-name", # Cloud SQL Instance Connection Name
562
+ "pg8000",
563
+ user="my-user",
564
+ password="my-password",
565
+ db="my-database",
566
+ ip_type="public" # "private" for private IP
567
+ ),
568
+ )
576
569
 
577
570
  # create SQLAlchemy ORM session
578
571
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@@ -641,40 +634,29 @@ async def main():
641
634
  #### SQLAlchemy Async Engine
642
635
 
643
636
  ```python
644
- import asyncpg
645
-
646
637
  import sqlalchemy
647
638
  from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
648
639
 
649
640
  from google.cloud.sql.connector import Connector, create_async_connector
650
641
 
651
- async def init_connection_pool(connector: Connector) -> AsyncEngine:
652
- # creation function to generate asyncpg connections as 'async_creator' arg
653
- async def getconn() -> asyncpg.Connection:
654
- conn: asyncpg.Connection = await connector.connect_async(
642
+
643
+ async def main():
644
+ # initialize Connector object for connections to Cloud SQL
645
+ connector = await create_async_connector()
646
+
647
+ # The Cloud SQL Python Connector can be used along with SQLAlchemy using the
648
+ # 'async_creator' argument to 'create_async_engine'
649
+ pool = create_async_engine(
650
+ "postgresql+asyncpg://",
651
+ async_creator=lambda: connector.connect_async(
655
652
  "project:region:instance", # Cloud SQL instance connection name
656
653
  "asyncpg",
657
654
  user="my-user",
658
655
  password="my-password",
659
656
  db="my-db-name"
660
657
  # ... additional database driver args
661
- )
662
- return conn
663
-
664
- # The Cloud SQL Python Connector can be used along with SQLAlchemy using the
665
- # 'async_creator' argument to 'create_async_engine'
666
- pool = create_async_engine(
667
- "postgresql+asyncpg://",
668
- async_creator=getconn,
658
+ ),
669
659
  )
670
- return pool
671
-
672
- async def main():
673
- # initialize Connector object for connections to Cloud SQL
674
- connector = await create_async_connector()
675
-
676
- # initialize connection pool
677
- pool = await init_connection_pool(connector)
678
660
 
679
661
  # example query
680
662
  async with pool.connect() as conn:
@@ -745,33 +727,24 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
745
727
 
746
728
  from google.cloud.sql.connector import Connector
747
729
 
748
- async def init_connection_pool(connector: Connector) -> AsyncEngine:
749
- # creation function to generate asyncpg connections as 'async_creator' arg
750
- async def getconn() -> asyncpg.Connection:
751
- conn: asyncpg.Connection = await connector.connect_async(
752
- "project:region:instance", # Cloud SQL instance connection name
753
- "asyncpg",
754
- user="my-user",
755
- password="my-password",
756
- db="my-db-name"
757
- # ... additional database driver args
758
- )
759
- return conn
760
-
761
- # The Cloud SQL Python Connector can be used along with SQLAlchemy using the
762
- # 'async_creator' argument to 'create_async_engine'
763
- pool = create_async_engine(
764
- "postgresql+asyncpg://",
765
- async_creator=getconn,
766
- )
767
- return pool
768
730
 
769
731
  async def main():
770
732
  # initialize Connector object for connections to Cloud SQL
771
733
  loop = asyncio.get_running_loop()
772
734
  async with Connector(loop=loop) as connector:
773
- # initialize connection pool
774
- pool = await init_connection_pool(connector)
735
+ # The Cloud SQL Python Connector can be used along with SQLAlchemy using the
736
+ # 'async_creator' argument to 'create_async_engine'
737
+ pool = create_async_engine(
738
+ "postgresql+asyncpg://",
739
+ async_creator=lambda: connector.connect_async(
740
+ "project:region:instance", # Cloud SQL instance connection name
741
+ "asyncpg",
742
+ user="my-user",
743
+ password="my-password",
744
+ db="my-db-name"
745
+ # ... additional database driver args
746
+ ),
747
+ )
775
748
 
776
749
  # example query
777
750
  async with pool.connect() as conn:
@@ -0,0 +1,25 @@
1
+ cloud_sql_python_connector-1.18.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
2
+ google/cloud/sql/connector/__init__.py,sha256=w6w35V-m2QlpcL-ShKd2BvNF-xXOwJ2FoidE7npPE7c,1148
3
+ google/cloud/sql/connector/asyncpg.py,sha256=v4ohOnSYjHTOvJPhTE761xP1KKo4FUJQW_WAnNW_2g4,1918
4
+ google/cloud/sql/connector/client.py,sha256=uVOMnRe1AxiSh6BnK4-SuSYrRID_jnm7SgMMN6s5jp4,13076
5
+ google/cloud/sql/connector/connection_info.py,sha256=BSuW-ovtEUYTIvDzQkaE3GofCs3Xoi0TtztFaAq4Kvo,4868
6
+ google/cloud/sql/connector/connection_name.py,sha256=bI4M3Qa2mtBHyD08oF41FF5W9FBOXWu4dGGg50Na_A4,2606
7
+ google/cloud/sql/connector/connector.py,sha256=jukyYD75FElLvKgSHg4o91Wug21jIn87nJWNbeoTZUc,24183
8
+ google/cloud/sql/connector/enums.py,sha256=dVBS9eEWNrHtzze0AJlmtbdUogG3EocNWOQcI_8qPd0,2937
9
+ google/cloud/sql/connector/exceptions.py,sha256=QdQBzZ3t1RE5V744wW4sGaucWD2cEedB-dWNCkh8SAA,1974
10
+ google/cloud/sql/connector/instance.py,sha256=jDEQf-OXFzqKO9a4bnzRQ0qUyO3_xKuiEIRWc_7wpyE,8694
11
+ google/cloud/sql/connector/lazy.py,sha256=oB6F-O2fk8ZguY4J7Vrts91bqbVb6P5wj72Lf6UJIQA,4948
12
+ google/cloud/sql/connector/monitored_cache.py,sha256=LFYjX8hsdN_Dt0cMwLSWgAhWWQpZ35qA0CLlpc6xhsE,5587
13
+ google/cloud/sql/connector/pg8000.py,sha256=TnS_SNbgY19oNEawOx-qJyW-f_k42-SnR3VTBOSr5FI,1717
14
+ google/cloud/sql/connector/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ google/cloud/sql/connector/pymysql.py,sha256=PrTQ1lB5EWAcN4BPX_5hb-WerncS4X76bBk4y0es0ks,1948
16
+ google/cloud/sql/connector/pytds.py,sha256=KZxmJQQG9XfmsmUoEGX7RpMirWdoreJ2-_FuHO6Td-s,2280
17
+ google/cloud/sql/connector/rate_limiter.py,sha256=zqZ9pVXHan4LWoGT8Bq6U3HpZcrdzKhlXVYwsX3ftA0,2928
18
+ google/cloud/sql/connector/refresh_utils.py,sha256=Y6pT4X13b0IozmRMPuhQJRqacaGubn_mjzpFFjrXonM,5122
19
+ google/cloud/sql/connector/resolver.py,sha256=1KYN0E-oTca8wRyW2nqCw56XRHtq-QGYpPvkk-UhjnY,3290
20
+ google/cloud/sql/connector/utils.py,sha256=AA_oBBxRuULb7-hHHK8HQJU5fttzBpzdIZwMNZ5kNCM,3473
21
+ google/cloud/sql/connector/version.py,sha256=EN8WaWofBDH6G6c_3vog4YS3q3OoSo7ILwETmMo8ZT0,598
22
+ cloud_sql_python_connector-1.18.1.dist-info/METADATA,sha256=X6kdCzO2GF6gry0g9A7P8EJ1RNoTSHBJF5ft5y5PRH8,30340
23
+ cloud_sql_python_connector-1.18.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
24
+ cloud_sql_python_connector-1.18.1.dist-info/top_level.txt,sha256=_1QvSJIhFAGfxb79D6DhB7SUw2X6T4rwnz_LLrbcD3c,7
25
+ cloud_sql_python_connector-1.18.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,4 +1,4 @@
1
- """"
1
+ """
2
2
  Copyright 2019 Google LLC
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,21 +28,22 @@ async def connect(
28
28
  ) -> "asyncpg.Connection":
29
29
  """Helper function to create an asyncpg DB-API connection object.
30
30
 
31
- :type ip_address: str
32
- :param ip_address: A string containing an IP address for the Cloud SQL
33
- instance.
31
+ Args:
32
+ ip_address (str): A string containing an IP address for the Cloud SQL
33
+ instance.
34
+ ctx (ssl.SSLContext): An SSLContext object created from the Cloud SQL
35
+ server CA cert and ephemeral cert.
36
+ server CA cert and ephemeral cert.
37
+ kwargs: Keyword arguments for establishing asyncpg connection
38
+ object to Cloud SQL instance.
34
39
 
35
- :type ctx: ssl.SSLContext
36
- :param ctx: An SSLContext object created from the Cloud SQL server CA
37
- cert and ephemeral cert.
38
-
39
- :type kwargs: Any
40
- :param kwargs: Keyword arguments for establishing asyncpg connection
41
- object to Cloud SQL instance.
42
-
43
- :rtype: asyncpg.Connection
44
- :returns: An asyncpg.Connection object to a Cloud SQL instance.
40
+ Returns:
41
+ asyncpg.Connection: An asyncpg connection to the Cloud SQL
42
+ instance.
43
+ Raises:
44
+ ImportError: The asyncpg module cannot be imported.
45
45
  """
46
+
46
47
  try:
47
48
  import asyncpg
48
49
  except ImportError:
@@ -59,8 +59,7 @@ class CloudSQLClient:
59
59
  driver: Optional[str] = None,
60
60
  user_agent: Optional[str] = None,
61
61
  ) -> None:
62
- """
63
- Establish the client to be used for Cloud SQL Admin API requests.
62
+ """Establishes the client to be used for Cloud SQL Admin API requests.
64
63
 
65
64
  Args:
66
65
  sqladmin_api_endpoint (str): Base URL to use when calling
@@ -100,24 +99,22 @@ class CloudSQLClient:
100
99
  region: str,
101
100
  instance: str,
102
101
  ) -> dict[str, Any]:
103
- """Requests metadata from the Cloud SQL Instance
104
- and returns a dictionary containing the IP addresses and certificate
105
- authority of the Cloud SQL Instance.
106
-
107
- :type project: str
108
- :param project:
109
- A string representing the name of the project.
102
+ """Requests metadata from the Cloud SQL Instance and returns a dictionary
103
+ containing the IP addresses and certificate authority of the Cloud SQL
104
+ Instance.
110
105
 
111
- :type region: str
112
- :param region : A string representing the name of the region.
106
+ Args:
107
+ project (str): A string representing the name of the project.
108
+ region (str): A string representing the name of the region.
109
+ instance (str): A string representing the name of the instance.
113
110
 
114
- :type instance: str
115
- :param instance: A string representing the name of the instance.
111
+ Returns:
112
+ A dictionary containing a dictionary of all IP addresses
113
+ and their type and a string representing the certificate authority.
116
114
 
117
- :rtype: dict[str: Union[dict, str]]
118
- :returns: Returns a dictionary containing a dictionary of all IP
119
- addresses and their type and a string representing the
120
- certificate authority.
115
+ Raises:
116
+ ValueError: Provided region does not match the region of the
117
+ Cloud SQL instance.
121
118
  """
122
119
 
123
120
  headers = {
@@ -156,10 +153,23 @@ class CloudSQLClient:
156
153
  # resolve dnsName into IP address for PSC
157
154
  # Note that we have to check for PSC enablement also because CAS
158
155
  # instances also set the dnsName field.
159
- # Remove trailing period from DNS name. Required for SSL in Python
160
- dns_name = ret_dict.get("dnsName", "").rstrip(".")
161
- if dns_name and ret_dict.get("pscEnabled"):
162
- ip_addresses["PSC"] = dns_name
156
+ if ret_dict.get("pscEnabled"):
157
+ # Find PSC instance DNS name in the dns_names field
158
+ psc_dns_names = [
159
+ d["name"]
160
+ for d in ret_dict.get("dnsNames", [])
161
+ if d["connectionType"] == "PRIVATE_SERVICE_CONNECT"
162
+ and d["dnsScope"] == "INSTANCE"
163
+ ]
164
+ dns_name = psc_dns_names[0] if psc_dns_names else None
165
+
166
+ # Fall back do dns_name field if dns_names is not set
167
+ if dns_name is None:
168
+ dns_name = ret_dict.get("dnsName", None)
169
+
170
+ # Remove trailing period from DNS name. Required for SSL in Python
171
+ if dns_name:
172
+ ip_addresses["PSC"] = dns_name.rstrip(".")
163
173
 
164
174
  return {
165
175
  "ip_addresses": ip_addresses,
@@ -176,23 +186,17 @@ class CloudSQLClient:
176
186
  ) -> tuple[str, datetime.datetime]:
177
187
  """Asynchronously requests an ephemeral certificate from the Cloud SQL Instance.
178
188
 
179
- :type project: str
180
- :param project : A string representing the name of the project.
181
-
182
- :type instance: str
183
- :param instance: A string representing the name of the instance.
184
-
185
- :type pub_key:
186
- :param str: A string representing PEM-encoded RSA public key.
187
-
188
- :type enable_iam_auth: bool
189
- :param enable_iam_auth
190
- Enables automatic IAM database authentication for Postgres or MySQL
191
- instances.
189
+ Args:
190
+ project (str): A string representing the name of the project.
191
+ instance (str): string representing the name of the instance.
192
+ pub_key (str): A string representing PEM-encoded RSA public key.
193
+ enable_iam_auth (bool): Enables automatic IAM database
194
+ authentication for Postgres or MySQL instances.
192
195
 
193
- :rtype: str
194
- :returns: An ephemeral certificate from the Cloud SQL instance that allows
195
- authorized connections to the instance.
196
+ Returns:
197
+ A tuple containing an ephemeral certificate from
198
+ the Cloud SQL instance as well as a datetime object
199
+ representing the expiration time of the certificate.
196
200
  """
197
201
  headers = {
198
202
  "Authorization": f"Bearer {self._credentials.token}",
@@ -14,6 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import abc
17
18
  from dataclasses import dataclass
18
19
  import logging
19
20
  import ssl
@@ -34,6 +35,27 @@ if TYPE_CHECKING:
34
35
  logger = logging.getLogger(name=__name__)
35
36
 
36
37
 
38
+ class ConnectionInfoCache(abc.ABC):
39
+ """Abstract class for Connector connection info caches."""
40
+
41
+ @abc.abstractmethod
42
+ async def connect_info(self) -> ConnectionInfo:
43
+ pass
44
+
45
+ @abc.abstractmethod
46
+ async def force_refresh(self) -> None:
47
+ pass
48
+
49
+ @abc.abstractmethod
50
+ async def close(self) -> None:
51
+ pass
52
+
53
+ @property
54
+ @abc.abstractmethod
55
+ def closed(self) -> bool:
56
+ pass
57
+
58
+
37
59
  @dataclass
38
60
  class ConnectionInfo:
39
61
  """Contains all necessary information to connect securely to the
@@ -19,6 +19,10 @@ import re
19
19
  # Additionally, we have to support legacy "domain-scoped" projects
20
20
  # (e.g. "google.com:PROJECT")
21
21
  CONN_NAME_REGEX = re.compile(("([^:]+(:[^:]+)?):([^:]+):([^:]+)"))
22
+ # The domain name pattern in accordance with RFC 1035, RFC 1123 and RFC 2181.
23
+ DOMAIN_NAME_REGEX = re.compile(
24
+ r"^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$"
25
+ )
22
26
 
23
27
 
24
28
  @dataclass
@@ -38,6 +42,16 @@ class ConnectionName:
38
42
  return f"{self.domain_name} -> {self.project}:{self.region}:{self.instance_name}"
39
43
  return f"{self.project}:{self.region}:{self.instance_name}"
40
44
 
45
+ def get_connection_string(self) -> str:
46
+ """Get the instance connection string for the Cloud SQL instance."""
47
+ return f"{self.project}:{self.region}:{self.instance_name}"
48
+
49
+
50
+ def _is_valid_domain(domain_name: str) -> bool:
51
+ if DOMAIN_NAME_REGEX.fullmatch(domain_name) is None:
52
+ return False
53
+ return True
54
+
41
55
 
42
56
  def _parse_connection_name(connection_name: str) -> ConnectionName:
43
57
  return _parse_connection_name_with_domain_name(connection_name, "")