Skip to content

Commit 0ce744f

Browse files
authored
fix: Cache AWS Creds instead of getting a Session on every invoke (aws#1494)
Addresses Issue aws#1375. Due to Boto3.session.Session objects not being thread safe, we create a Session object on every invoke to get credentials to mount into the container. This works well except for customers using MFA. When using MFA, this dehavior requires customers to provide MFA code/pin on every invoke. This creates much longer debug sessions for customers and pauses invokes until the MFA code/pin is provided. This PR changes that behavior to one we had between version v0.8.0 and v0.16.0, which is caching the creds/session to be reused on other invokes until the credentials expire. Once the credentials expire, customers will need to restart the command (which was the same behavior on version 0.8.0 to 0.16.0.
1 parent 4f2c345 commit 0ce744f

File tree

2 files changed

+25
-16
lines changed

2 files changed

+25
-16
lines changed

samcli/commands/local/lib/local_lambda.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def __init__(
5353
self.aws_region = aws_region
5454
self.env_vars_values = env_vars_values or {}
5555
self.debug_context = debug_context
56+
self._boto3_session_creds = None
57+
self._boto3_region = None
5658

5759
def invoke(self, function_name, event, stdout=None, stderr=None):
5860
"""
@@ -205,6 +207,22 @@ def _make_env_vars(self, function):
205207
aws_creds=aws_creds,
206208
)
207209

210+
def _get_session_creds(self):
211+
if self._boto3_session_creds is None:
212+
# to pass command line arguments for region & profile to setup boto3 default session
213+
LOG.debug("Loading AWS credentials from session with profile '%s'", self.aws_profile)
214+
session = boto3.session.Session(profile_name=self.aws_profile, region_name=self.aws_region)
215+
216+
# check for region_name in session and cache
217+
if hasattr(session, "region_name") and session.region_name:
218+
self._boto3_region = session.region_name
219+
220+
# don't set cached session creds if there is not a session
221+
if session:
222+
self._boto3_session_creds = session.get_credentials()
223+
224+
return self._boto3_session_creds
225+
208226
def get_aws_creds(self):
209227
"""
210228
Returns AWS credentials obtained from the shell environment or given profile
@@ -215,26 +233,17 @@ def get_aws_creds(self):
215233
"""
216234
result = {}
217235

218-
# to pass command line arguments for region & profile to setup boto3 default session
219-
LOG.debug("Loading AWS credentials from session with profile '%s'", self.aws_profile)
220-
# boto3.session.Session is not thread safe. To ensure we do not run into a race condition with start-lambda
221-
# or start-api, we create the session object here on every invoke.
222-
session = boto3.session.Session(profile_name=self.aws_profile, region_name=self.aws_region)
223-
224-
if not session:
225-
return result
226-
227236
# Load the credentials from profile/environment
228-
creds = session.get_credentials()
237+
creds = self._get_session_creds()
238+
239+
# After loading credentials, region name might be available here.
240+
if self._boto3_region:
241+
result["region"] = self._boto3_region
229242

230243
if not creds:
231-
# If we were unable to load credentials, then just return empty. We will use the default
244+
# If we were unable to load credentials, then just return result. We will use the default
232245
return result
233246

234-
# After loading credentials, region name might be available here.
235-
if hasattr(session, "region_name") and session.region_name:
236-
result["region"] = session.region_name
237-
238247
# Only add the key, if its value is present
239248
if hasattr(creds, "access_key") and creds.access_key:
240249
result["key"] = creds.access_key

tests/unit/commands/local/lib/test_local_lambda.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def test_must_work_with_no_credentials(self, boto3_mock):
144144
boto3_mock.session.Session.return_value = mock_session
145145
mock_session.get_credentials.return_value = None
146146

147-
expected = {}
147+
expected = {"region": mock_session.region_name}
148148
actual = self.local_lambda.get_aws_creds()
149149
self.assertEqual(expected, actual)
150150

0 commit comments

Comments
 (0)