-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Skip functions/layers pointing to S3 location in providers #2756
Changes from 1 commit
b088af7
4a5b6da
67c61f9
f13ad32
21183f0
5313334
10182dc
980d895
880f057
4a0cd6c
1ce6126
8632564
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,13 @@ class SamBaseProvider: | |
CLOUDFORMATION_STACK = AWS_CLOUDFORMATION_STACK | ||
DEFAULT_CODEURI = "." | ||
|
||
CODE_PROPERTY_KEYS = { | ||
LAMBDA_FUNCTION: "Code", | ||
SERVERLESS_FUNCTION: "CodeUri", | ||
LAMBDA_LAYER: "Content", | ||
SERVERLESS_LAYER: "ContentUri", | ||
} | ||
|
||
def get(self, name: str) -> Optional[Any]: | ||
""" | ||
Given name of the function, this method must return the Function object | ||
|
@@ -45,9 +52,9 @@ def get_all(self) -> Iterable: | |
raise NotImplementedError("not implemented") | ||
|
||
@staticmethod | ||
def _extract_lambda_function_code(resource_properties: Dict, code_property_key: str) -> str: | ||
def _extract_codeuri(resource_properties: Dict, code_property_key: str) -> str: | ||
""" | ||
Extracts the Lambda Function Code from the Resource Properties | ||
Extracts the Function/Layer code path from the Resource Properties | ||
|
||
Parameters | ||
---------- | ||
|
@@ -61,14 +68,28 @@ def _extract_lambda_function_code(resource_properties: Dict, code_property_key: | |
str | ||
Representing the local code path | ||
""" | ||
|
||
codeuri = resource_properties.get(code_property_key, SamBaseProvider.DEFAULT_CODEURI) | ||
|
||
if isinstance(codeuri, dict): | ||
return SamBaseProvider.DEFAULT_CODEURI | ||
|
||
return cast(str, codeuri) | ||
|
||
@staticmethod | ||
def _is_code_uri_s3(codeuri: Optional[Any]) -> bool: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: isn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can codeuri be anything other than None, dict, str? Shouldn't we use something like Optional[Union[str, Dict]]? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yeah right. I changed to |
||
return (isinstance(codeuri, dict) and ("S3Bucket" in codeuri or "Bucket" in codeuri)) or ( | ||
isinstance(codeuri, str) and codeuri.startswith("s3://") | ||
) | ||
|
||
@staticmethod | ||
def _warn_code_extraction(resource_type: str, resource_name: str, code_property: str) -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels slightly weird to have the logging specifically refactored out. Doesn't seem very meaningful on its own. Would it make sense to (e.g.) instead have a method whose sole purpose is to check whether a resource uses an S3-based There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should do something like we did in #2691 (we had similar discussion before). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah so I think the whole flow in these methods feels weird as we're essentially dealing with two very similar abstractions ( Not in this scope so I'm happy with this for now, but ideally I'm thinking we would have a well-defined abstraction to deal with functions, e.g. by wrapping them or performing a translation beforehand so we only deal with a subset of well-defined resource types with well-defined operations, so that we don't need all this |
||
LOG.warning( | ||
"%s '%s' has specified S3 location for %s which is unsupported.", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This warning feels a bit ambiguous here. Shouldn't we specify the scope of this "unsupported"? |
||
resource_type, | ||
resource_name, | ||
code_property, | ||
) | ||
|
||
@staticmethod | ||
def _extract_lambda_function_imageuri(resource_properties: Dict, code_property_key: str) -> Optional[str]: | ||
""" | ||
|
@@ -107,45 +128,6 @@ def _extract_sam_function_imageuri(resource_properties: Dict, code_property_key: | |
""" | ||
return resource_properties.get(code_property_key, None) | ||
|
||
@staticmethod | ||
def _extract_sam_function_codeuri( | ||
name: str, | ||
resource_properties: Dict[str, Any], | ||
code_property_key: str, | ||
ignore_code_extraction_warnings: bool = False, | ||
) -> str: | ||
""" | ||
Extracts the SAM Function CodeUri from the Resource Properties | ||
|
||
Parameters | ||
---------- | ||
name str | ||
LogicalId of the resource | ||
resource_properties dict | ||
Dictionary representing the Properties of the Resource | ||
code_property_key str | ||
Property Key of the code on the Resource | ||
ignore_code_extraction_warnings | ||
Boolean to ignore log statements on code extraction from Resources. | ||
|
||
Returns | ||
------- | ||
str | ||
Representing the local code path | ||
""" | ||
codeuri = resource_properties.get(code_property_key, SamBaseProvider.DEFAULT_CODEURI) | ||
# CodeUri can be a dictionary of S3 Bucket/Key or a S3 URI, neither of which are supported | ||
if isinstance(codeuri, dict) or (isinstance(codeuri, str) and codeuri.startswith("s3://")): | ||
if not ignore_code_extraction_warnings: | ||
LOG.warning( | ||
"Lambda function '%s' has specified S3 location for CodeUri which is unsupported. " | ||
"Using default value of '%s' instead", | ||
name, | ||
SamBaseProvider.DEFAULT_CODEURI, | ||
) | ||
return SamBaseProvider.DEFAULT_CODEURI | ||
Comment on lines
-140
to
-146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. explain: now the warning is being handled by |
||
return cast(str, codeuri) | ||
|
||
@staticmethod | ||
def get_template(template_dict: Dict, parameter_overrides: Optional[Dict[str, str]] = None) -> Dict: | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -129,6 +129,14 @@ def _extract_functions( | |
if resource_metadata: | ||
resource_properties["Metadata"] = resource_metadata | ||
|
||
if resource_type in [SamFunctionProvider.SERVERLESS_FUNCTION, SamFunctionProvider.LAMBDA_FUNCTION]: | ||
code_property_key = SamBaseProvider.CODE_PROPERTY_KEYS[resource_type] | ||
if SamBaseProvider._is_code_uri_s3(resource_properties.get(code_property_key)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we rename this method? _is_code_uri_s3, we explicitly take in the key to look up in the function. it could be CodeUri, ContentUri, Code, Content. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch. Renamed to @staticmethod
def _is_s3_location(location: Optional[Union[str, Dict]]) -> bool:
"""
the input could be:
- CodeUri of Serverless::Function
- Code of Lambda::Function
- ContentUri of Serverless::LayerVersion
- Content of Lambda::LayerVersion
"""
return (isinstance(location, dict) and ("S3Bucket" in location or "Bucket" in location)) or (
isinstance(location, str) and location.startswith("s3://")
) |
||
# CodeUri can be a dictionary of S3 Bucket/Key or a S3 URI, neither of which are supported | ||
if not ignore_code_extraction_warnings: | ||
SamFunctionProvider._warn_code_extraction(resource_type, name, code_property_key) | ||
continue | ||
|
||
if resource_type == SamFunctionProvider.SERVERLESS_FUNCTION: | ||
layers = SamFunctionProvider._parse_layer_info( | ||
stack, | ||
|
@@ -142,7 +150,6 @@ def _extract_functions( | |
resource_properties, | ||
layers, | ||
use_raw_codeuri, | ||
ignore_code_extraction_warnings=ignore_code_extraction_warnings, | ||
) | ||
result[function.full_path] = function | ||
|
||
|
@@ -169,7 +176,6 @@ def _convert_sam_function_resource( | |
resource_properties: Dict, | ||
layers: List[LayerVersion], | ||
use_raw_codeuri: bool = False, | ||
ignore_code_extraction_warnings: bool = False, | ||
) -> Function: | ||
""" | ||
Converts a AWS::Serverless::Function resource to a Function configuration usable by the provider. | ||
|
@@ -197,12 +203,7 @@ def _convert_sam_function_resource( | |
LOG.debug("Found Serverless function with name='%s' and InlineCode", name) | ||
codeuri = None | ||
else: | ||
codeuri = SamFunctionProvider._extract_sam_function_codeuri( | ||
name, | ||
resource_properties, | ||
"CodeUri", | ||
ignore_code_extraction_warnings=ignore_code_extraction_warnings, | ||
) | ||
codeuri = SamBaseProvider._extract_codeuri(resource_properties, "CodeUri") | ||
LOG.debug("Found Serverless function with name='%s' and CodeUri='%s'", name, codeuri) | ||
elif packagetype == IMAGE: | ||
imageuri = SamFunctionProvider._extract_sam_function_imageuri(resource_properties, "ImageUri") | ||
|
@@ -252,7 +253,7 @@ def _convert_lambda_function_resource( | |
LOG.debug("Found Lambda function with name='%s' and Code ZipFile", name) | ||
codeuri = None | ||
else: | ||
codeuri = SamFunctionProvider._extract_lambda_function_code(resource_properties, "Code") | ||
codeuri = SamBaseProvider._extract_codeuri(resource_properties, "Code") | ||
LOG.debug("Found Lambda function with name='%s' and CodeUri='%s'", name, codeuri) | ||
elif packagetype == IMAGE: | ||
imageuri = SamFunctionProvider._extract_lambda_function_imageuri(resource_properties, "Code") | ||
|
@@ -390,7 +391,8 @@ def _parse_layer_info( | |
found_layer = SamFunctionProvider._locate_layer_from_ref( | ||
stack, layer, use_raw_codeuri, ignore_code_extraction_warnings | ||
) | ||
layers.append(found_layer) | ||
if found_layer: | ||
layers.append(found_layer) | ||
else: | ||
LOG.debug( | ||
'layer "%s" is not recognizable, ' | ||
|
@@ -403,7 +405,7 @@ def _parse_layer_info( | |
@staticmethod | ||
def _locate_layer_from_ref( | ||
stack: Stack, layer: Dict, use_raw_codeuri: bool = False, ignore_code_extraction_warnings: bool = False | ||
) -> LayerVersion: | ||
) -> Optional[LayerVersion]: | ||
layer_logical_id = cast(str, layer.get("Ref")) | ||
layer_resource = stack.resources.get(layer_logical_id) | ||
if not layer_resource or layer_resource.get("Type", "") not in ( | ||
|
@@ -417,13 +419,14 @@ def _locate_layer_from_ref( | |
compatible_runtimes = layer_properties.get("CompatibleRuntimes") | ||
codeuri: Optional[str] = None | ||
|
||
if resource_type == SamFunctionProvider.LAMBDA_LAYER: | ||
codeuri = SamFunctionProvider._extract_lambda_function_code(layer_properties, "Content") | ||
|
||
if resource_type == SamFunctionProvider.SERVERLESS_LAYER: | ||
codeuri = SamFunctionProvider._extract_sam_function_codeuri( | ||
layer_logical_id, layer_properties, "ContentUri", ignore_code_extraction_warnings | ||
) | ||
if resource_type in [SamFunctionProvider.LAMBDA_LAYER, SamFunctionProvider.SERVERLESS_LAYER]: | ||
code_property_key = SamBaseProvider.CODE_PROPERTY_KEYS[resource_type] | ||
if SamBaseProvider._is_code_uri_s3(layer_properties.get(code_property_key)): | ||
# Content can be a dictionary of S3 Bucket/Key or a S3 URI, neither of which are supported | ||
if not ignore_code_extraction_warnings: | ||
SamFunctionProvider._warn_code_extraction(resource_type, layer_logical_id, code_property_key) | ||
return None | ||
codeuri = SamBaseProvider._extract_codeuri(layer_properties, code_property_key) | ||
|
||
if codeuri and not use_raw_codeuri: | ||
LOG.debug("--base-dir is presented not, adjusting uri %s relative to %s", codeuri, stack.location) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's some grammar issue on this sentence. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sam package
already has a bunch of these constants for package-able code paths, would it make sense to refactor that, so thats its usable across the code base?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have another PR ready for this purpose (coming right after this PR is merged)