Skip to content

Commit cb0185c

Browse files
rohangujarathiRohan Gujarathi
and
Rohan Gujarathi
authored
fix: update lambda function when function arn is provided (#3668)
Co-authored-by: Rohan Gujarathi <[email protected]>
1 parent b917960 commit cb0185c

File tree

4 files changed

+87
-9
lines changed

4 files changed

+87
-9
lines changed

src/sagemaker/lambda_helper.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ def __init__(
9292
if handler is None:
9393
raise ValueError("Lambda handler must be provided.")
9494

95+
if function_arn is not None:
96+
if zipped_code_dir and script:
97+
raise ValueError("Provide either script or zipped_code_dir, not both.")
98+
9599
def create(self):
96100
"""Method to create a lambda function.
97101
@@ -140,17 +144,29 @@ def update(self):
140144
try:
141145
if self.script is not None:
142146
response = lambda_client.update_function_code(
143-
FunctionName=self.function_name, ZipFile=_zip_lambda_code(self.script)
147+
FunctionName=self.function_name or self.function_arn,
148+
ZipFile=_zip_lambda_code(self.script),
144149
)
145150
else:
151+
bucket = self.s3_bucket or self.session.default_bucket()
152+
# get function name to be used in S3 upload path
153+
if self.function_arn:
154+
versioned_function_name = self.function_arn.split("funtion:")[-1]
155+
if ":" in versioned_function_name:
156+
function_name_for_s3 = versioned_function_name.split(":")[0]
157+
else:
158+
function_name_for_s3 = versioned_function_name
159+
else:
160+
function_name_for_s3 = self.function_name
161+
146162
response = lambda_client.update_function_code(
147163
FunctionName=(self.function_name or self.function_arn),
148-
S3Bucket=self.s3_bucket,
164+
S3Bucket=bucket,
149165
S3Key=_upload_to_s3(
150166
s3_client=_get_s3_client(self.session),
151-
function_name=self.function_name,
167+
function_name=function_name_for_s3,
152168
zipped_code_dir=self.zipped_code_dir,
153-
s3_bucket=self.s3_bucket,
169+
s3_bucket=bucket,
154170
),
155171
)
156172
return response

src/sagemaker/workflow/lambda_step.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from typing import List, Dict, Optional, Union
1717
from enum import Enum
18+
import warnings
1819

1920
import attr
2021

@@ -152,10 +153,20 @@ def to_request(self) -> RequestType:
152153
def _get_function_arn(self):
153154
"""Returns the lambda function arn
154155
155-
Method creates a lambda function and returns it's arn.
156-
If the lambda is already present, it will build it's arn and return that.
156+
It upserts a lambda function if function name is provided.
157+
It updates a lambda function if lambda arn and code is provided.
158+
It is a no-op if code is not provided but function arn is provided.
157159
"""
158160
if self.lambda_func.function_arn is None:
159161
response = self.lambda_func.upsert()
160162
return response["FunctionArn"]
161-
return self.lambda_func.function_arn
163+
164+
if self.lambda_func.zipped_code_dir is None and self.lambda_func.script is None:
165+
warnings.warn(
166+
"Lambda function won't be updated because zipped_code_dir \
167+
or script is not provided."
168+
)
169+
return self.lambda_func.function_arn
170+
171+
response = self.lambda_func.update()
172+
return response["FunctionArn"]

tests/unit/sagemaker/workflow/test_lambda_step.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,12 @@ def test_lambda_step_no_inputs_outputs(sagemaker_session):
203203
}
204204

205205

206-
def test_lambda_step_with_function_arn(sagemaker_session):
206+
def test_lambda_step_with_function_arn_no_lambda_update(sagemaker_session):
207207
lambda_func = MagicMock(
208208
function_arn="arn:aws:lambda:us-west-2:123456789012:function:sagemaker_test_lambda",
209209
session=sagemaker_session,
210+
zipped_code_dir=None,
211+
script=None,
210212
)
211213
lambda_step = LambdaStep(
212214
name="MyLambdaStep",
@@ -220,6 +222,24 @@ def test_lambda_step_with_function_arn(sagemaker_session):
220222
lambda_func.upsert.assert_not_called()
221223

222224

225+
def test_lambda_step_with_function_arn_lambda_updated(sagemaker_session):
226+
lambda_func = MagicMock(
227+
function_arn="arn:aws:lambda:us-west-2:123456789012:function:sagemaker_test_lambda",
228+
zipped_code_dir=None,
229+
script="code",
230+
session=sagemaker_session,
231+
)
232+
lambda_step = LambdaStep(
233+
name="MyLambdaStep",
234+
depends_on=["TestStep"],
235+
lambda_func=lambda_func,
236+
inputs={},
237+
outputs=[],
238+
)
239+
lambda_step._get_function_arn()
240+
lambda_func.update.assert_called_once()
241+
242+
223243
def test_lambda_step_without_function_arn(sagemaker_session):
224244
lambda_func = MagicMock(
225245
function_arn=None,

tests/unit/test_lambda_helper.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def sagemaker_session():
4141
boto_region_name=REGION,
4242
config=None,
4343
local_mode=False,
44+
# default_bucket=S3_BUCKET,
4445
)
4546
return session_mock
4647

@@ -117,7 +118,7 @@ def test_lambda_object_no_code_error():
117118
assert "Either zipped_code_dir or script must be provided" in str(error)
118119

119120

120-
def test_lambda_object_both_script_and_code_dir_error():
121+
def test_lambda_object_both_script_and_code_dir_error_with_name():
121122
with pytest.raises(ValueError) as error:
122123
lambda_helper.Lambda(
123124
function_name=FUNCTION_NAME,
@@ -130,6 +131,17 @@ def test_lambda_object_both_script_and_code_dir_error():
130131
assert "Provide either script or zipped_code_dir, not both." in str(error)
131132

132133

134+
def test_lambda_object_both_script_and_code_dir_error_with_arn():
135+
with pytest.raises(ValueError) as error:
136+
lambda_helper.Lambda(
137+
function_arn=LAMBDA_ARN,
138+
script=SCRIPT,
139+
zipped_code_dir=ZIPPED_CODE_DIR,
140+
session=sagemaker_session,
141+
)
142+
assert "Provide either script or zipped_code_dir, not both." in str(error)
143+
144+
133145
def test_lambda_object_no_handler_error():
134146
with pytest.raises(ValueError) as error:
135147
lambda_helper.Lambda(
@@ -274,6 +286,25 @@ def test_update_lambda_happycase2(sagemaker_session):
274286
)
275287

276288

289+
@patch("sagemaker.lambda_helper._upload_to_s3", return_value=S3_KEY)
290+
def test_update_lambda_s3bucket_not_provided(s3_upload, sagemaker_session):
291+
lambda_obj = lambda_helper.Lambda(
292+
function_arn=LAMBDA_ARN,
293+
execution_role_arn=EXECUTION_ROLE,
294+
zipped_code_dir=ZIPPED_CODE_DIR,
295+
handler=HANDLER,
296+
session=sagemaker_session,
297+
)
298+
299+
lambda_obj.update()
300+
301+
sagemaker_session.lambda_client.update_function_code.assert_called_with(
302+
FunctionName=LAMBDA_ARN,
303+
S3Bucket=sagemaker_session.default_bucket(),
304+
S3Key=s3_upload.return_value,
305+
)
306+
307+
277308
@patch("sagemaker.lambda_helper._zip_lambda_code", return_value=ZIPPED_CODE)
278309
def test_update_lambda_client_error(sagemaker_session):
279310
lambda_obj = lambda_helper.Lambda(

0 commit comments

Comments
 (0)