Skip to content

Commit 7d34c59

Browse files
chore: update datetime objects to be datetime aware (#983)
1 parent 25a740a commit 7d34c59

File tree

5 files changed

+52
-20
lines changed

5 files changed

+52
-20
lines changed

google/cloud/sql/connector/refresh_utils.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,15 @@ async def _get_ephemeral(
190190

191191
# decode cert to read expiration
192192
x509 = load_pem_x509_certificate(ephemeral_cert.encode("UTF-8"), default_backend())
193-
expiration = x509.not_valid_after
193+
expiration = x509.not_valid_after_utc
194194
# for IAM authentication OAuth2 token is embedded in cert so it
195195
# must still be valid for successful connection
196196
if enable_iam_auth:
197197
token_expiration: datetime.datetime = login_creds.expiry
198+
# google.auth library strips timezone info for backwards compatibality
199+
# reasons with Python 2. Add it back to allow timezone aware datetimes.
200+
# Ref: https://github.com/googleapis/google-auth-library-python/blob/49a5ff7411a2ae4d32a7d11700f9f961c55406a9/google/auth/_helpers.py#L93-L99
201+
token_expiration = token_expiration.replace(tzinfo=datetime.timezone.utc)
198202
if expiration > token_expiration:
199203
expiration = token_expiration
200204
return ephemeral_cert, expiration
@@ -213,7 +217,9 @@ def _seconds_until_refresh(
213217
:returns: Time in seconds to wait before performing next refresh.
214218
"""
215219

216-
duration = int((expiration - datetime.datetime.utcnow()).total_seconds())
220+
duration = int(
221+
(expiration - datetime.datetime.now(datetime.timezone.utc)).total_seconds()
222+
)
217223

218224
# if certificate duration is less than 1 hour
219225
if duration < 3600:
@@ -230,7 +236,7 @@ async def _is_valid(task: asyncio.Task) -> bool:
230236
try:
231237
metadata = await task
232238
# only valid if now is before the cert expires
233-
if datetime.datetime.utcnow() < metadata.expiration:
239+
if datetime.datetime.now(datetime.timezone.utc) < metadata.expiration:
234240
return True
235241
except Exception:
236242
# supress any errors from task

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
release_status = "Development Status :: 5 - Production/Stable"
2727
dependencies = [
2828
"aiohttp",
29-
"cryptography>=38.0.3",
29+
"cryptography>=42.0.0",
3030
"Requests",
3131
"google-auth",
3232
]

tests/unit/mocks.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def __class__(self) -> Credentials:
5050
def refresh(self, request: Callable) -> None:
5151
"""Refreshes the access token."""
5252
self.token = "12345"
53-
self.expiry = datetime.datetime.utcnow() + datetime.timedelta(minutes=60)
53+
self.expiry = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
54+
minutes=60
55+
)
5456

5557
@property
5658
def expired(self) -> bool:
@@ -62,7 +64,9 @@ def expired(self) -> bool:
6264
"""
6365
if self.expiry is None:
6466
return False
65-
return False if self.expiry > datetime.datetime.utcnow() else True
67+
if self.expiry > datetime.datetime.now(datetime.timezone.utc):
68+
return False
69+
return True
6670

6771
@property
6872
def valid(self) -> bool:
@@ -108,11 +112,15 @@ def __init__(
108112

109113

110114
async def instance_metadata_success(*args: Any, **kwargs: Any) -> MockMetadata:
111-
return MockMetadata(datetime.datetime.utcnow() + datetime.timedelta(minutes=10))
115+
return MockMetadata(
116+
datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=10)
117+
)
112118

113119

114120
async def instance_metadata_expired(*args: Any, **kwargs: Any) -> MockMetadata:
115-
return MockMetadata(datetime.datetime.utcnow() - datetime.timedelta(minutes=10))
121+
return MockMetadata(
122+
datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(minutes=10)
123+
)
116124

117125

118126
async def instance_metadata_error(*args: Any, **kwargs: Any) -> None:
@@ -145,10 +153,10 @@ def generate_cert(
145153
.issuer_name(issuer)
146154
.public_key(key.public_key())
147155
.serial_number(x509.random_serial_number())
148-
.not_valid_before(datetime.datetime.utcnow())
156+
.not_valid_before(datetime.datetime.now(datetime.timezone.utc))
149157
.not_valid_after(
150158
# cert valid for 10 mins
151-
datetime.datetime.utcnow()
159+
datetime.datetime.now(datetime.timezone.utc)
152160
+ datetime.timedelta(minutes=60)
153161
)
154162
)
@@ -189,7 +197,7 @@ def client_key_signed_cert(
189197
.issuer_name(issuer)
190198
.public_key(client_key)
191199
.serial_number(x509.random_serial_number())
192-
.not_valid_before(datetime.datetime.utcnow())
200+
.not_valid_before(datetime.datetime.now(datetime.timezone.utc))
193201
.not_valid_after(cert._not_valid_after) # type: ignore
194202
)
195203
return (
@@ -258,7 +266,8 @@ def connect_settings(self, ip_addrs: Optional[Dict] = None) -> str:
258266
"cert": server_ca_cert,
259267
"instance": self.name,
260268
"expirationTime": str(
261-
datetime.datetime.utcnow() + datetime.timedelta(minutes=10)
269+
datetime.datetime.now(datetime.timezone.utc)
270+
+ datetime.timedelta(minutes=10)
262271
),
263272
},
264273
"dnsName": "abcde.12345.us-central1.sql.goog",
@@ -284,7 +293,8 @@ def generate_ephemeral(self, client_bytes: str) -> str:
284293
"kind": "sql#sslCert",
285294
"cert": ephemeral_cert,
286295
"expirationTime": str(
287-
datetime.datetime.utcnow() + datetime.timedelta(minutes=10)
296+
datetime.datetime.now(datetime.timezone.utc)
297+
+ datetime.timedelta(minutes=10)
288298
),
289299
}
290300
}

tests/unit/test_instance.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ async def test_perform_refresh(
257257
assert isinstance(instance_metadata, ConnectionInfo)
258258
# verify instance metadata expiration
259259
assert (
260-
mock_instance.cert._not_valid_after.replace(microsecond=0) # type: ignore
260+
mock_instance.cert._not_valid_after.replace(
261+
tzinfo=datetime.timezone.utc, microsecond=0 # type: ignore
262+
)
261263
== instance_metadata.expiration
262264
)
263265

@@ -273,7 +275,9 @@ async def test_perform_refresh_expiration(
273275
credentials expiration should be used.
274276
"""
275277
# set credentials expiration to 1 minute from now
276-
expiration = datetime.datetime.utcnow() + datetime.timedelta(minutes=1)
278+
expiration = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
279+
minutes=1
280+
)
277281
credentials = mocks.FakeCredentials(token="my-token", expiry=expiration)
278282
setattr(instance, "_enable_iam_auth", True)
279283
# set downscoped credential to mock

tests/unit/test_refresh_utils.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
limitations under the License.
1515
"""
1616
import asyncio
17-
from datetime import datetime
18-
from datetime import timedelta
17+
import datetime
1918
from typing import Any, no_type_check
2019

2120
import aiohttp
@@ -226,7 +225,11 @@ def test_seconds_until_refresh_over_1_hour() -> None:
226225
# using pytest.approx since sometimes can be off by a second
227226
assert (
228227
pytest.approx(
229-
_seconds_until_refresh(datetime.utcnow() + timedelta(minutes=62)), 1
228+
_seconds_until_refresh(
229+
datetime.datetime.now(datetime.timezone.utc)
230+
+ datetime.timedelta(minutes=62)
231+
),
232+
1,
230233
)
231234
== 31 * 60
232235
)
@@ -242,7 +245,11 @@ def test_seconds_until_refresh_under_1_hour_over_4_mins() -> None:
242245
# using pytest.approx since sometimes can be off by a second
243246
assert (
244247
pytest.approx(
245-
_seconds_until_refresh(datetime.utcnow() + timedelta(minutes=5)), 1
248+
_seconds_until_refresh(
249+
datetime.datetime.now(datetime.timezone.utc)
250+
+ datetime.timedelta(minutes=5)
251+
),
252+
1,
246253
)
247254
== 60
248255
)
@@ -254,4 +261,9 @@ def test_seconds_until_refresh_under_4_mins() -> None:
254261
255262
If expiration is under 4 minutes, should return 0.
256263
"""
257-
assert _seconds_until_refresh(datetime.utcnow() + timedelta(minutes=3)) == 0
264+
assert (
265+
_seconds_until_refresh(
266+
datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=3)
267+
)
268+
== 0
269+
)

0 commit comments

Comments
 (0)