-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(intrinsics): only resolve the selected Fn::If branch #9134
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
base: develop
Are you sure you want to change the base?
Changes from all commits
d4c07ac
16c70d0
e16ec5b
0a7b27d
88ffaea
57b4fbd
b63826a
ace7482
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -224,7 +224,10 @@ def intrinsic_property_resolver(self, intrinsic, ignore_errors, parent_function= | |
| sanitized_key, parent_function | ||
| ), | ||
| ) | ||
| sanitized_dict[sanitized_key] = sanitized_val | ||
| # A resolved value of None means the property was Fn::If-ed to AWS::NoValue. | ||
| # CloudFormation drops such properties entirely rather than keeping a null value. | ||
| if sanitized_val is not None: | ||
| sanitized_dict[sanitized_key] = sanitized_val | ||
| # On any exception, leave the key:val of the orginal intact and continue on. | ||
| # https://github.com/awslabs/aws-sam-cli/issues/1386 | ||
| except Exception: | ||
|
|
@@ -716,24 +719,14 @@ def handle_fn_if(self, intrinsic_value, ignore_errors): | |
| ------- | ||
| This will return value_if_true and value_if_false depending on how the condition is evaluated | ||
| """ | ||
| arguments = self.intrinsic_property_resolver( | ||
| intrinsic_value, ignore_errors, parent_function=IntrinsicResolver.FN_IF | ||
| ) | ||
| verify_intrinsic_type_list(arguments, IntrinsicResolver.FN_IF) | ||
| verify_number_arguments(arguments, IntrinsicResolver.FN_IF, num=3) | ||
| verify_intrinsic_type_list(intrinsic_value, IntrinsicResolver.FN_IF) | ||
| verify_number_arguments(intrinsic_value, IntrinsicResolver.FN_IF, num=3) | ||
|
|
||
| condition_name = self.intrinsic_property_resolver( | ||
| arguments[0], ignore_errors, parent_function=IntrinsicResolver.FN_IF | ||
| intrinsic_value[0], ignore_errors, parent_function=IntrinsicResolver.FN_IF | ||
| ) | ||
| verify_intrinsic_type_str(condition_name, IntrinsicResolver.FN_IF) | ||
|
|
||
| value_if_true = self.intrinsic_property_resolver( | ||
| arguments[1], ignore_errors, parent_function=IntrinsicResolver.FN_IF | ||
| ) | ||
| value_if_false = self.intrinsic_property_resolver( | ||
| arguments[2], ignore_errors, parent_function=IntrinsicResolver.FN_IF | ||
| ) | ||
|
|
||
| condition = self._conditions.get(condition_name) | ||
| verify_intrinsic_type_dict( | ||
| condition, | ||
|
|
@@ -750,7 +743,8 @@ def handle_fn_if(self, intrinsic_value, ignore_errors): | |
| message="The result of {} must evaluate to bool".format(IntrinsicResolver.FN_IF), | ||
| ) | ||
|
|
||
| return value_if_true if condition_evaluated else value_if_false | ||
| selected_value = intrinsic_value[1] if condition_evaluated else intrinsic_value[2] | ||
| return self.intrinsic_property_resolver(selected_value, ignore_errors, parent_function=IntrinsicResolver.FN_IF) | ||
|
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. [BUG] handle_fn_if can now return Before this change, a branch containing Now the selected branch is resolved directly, so Concrete failure — the common "conditionally omit a property" idiom: Properties:
Layers: !If [UseLayers, [!Ref MyLayer], !Ref "AWS::NoValue"]When Note the element-level form Two options:
Either way, please add a unit test covering an Note on the previous review comment: the handle_fn_getatt change that forwarded ignore_errors into resolve_symbols is no longer in the diff, and test_template_ignore_errors_leaves_unresolvable_layer_getatt_as_dict was added as a guard for the nested-stack layer behavior. That finding is resolved and I did not re-raise it. The PR description still describes the handle_fn_getatt change, so it is now out of date.
Author
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. Fixed 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. [BUG] When the selected branch is # intrinsic_property_resolver, dict branch
sanitized_val = self.intrinsic_property_resolver(val, ignore_errors, parent_function=parent_function)
...
sanitized_dict[sanitized_key] = sanitized_val # key kept, value is NoneThe key is retained rather than dropped, which does not match CloudFormation's Events:
ApiEvent: !If [UseApi, {Type: Api, Properties: {Path: /, Method: get}}, !Ref "AWS::NoValue"]With for , event in serverlessfunction_events.items():
event_type = event.get(self._EVENT_TYPE) # AttributeError: 'NoneType' object has no attribute 'get'Previously that entry stayed as the raw Normalizing at the resolver would fix the whole class at once, and there is already precedent for exactly this in the codebase —
Author
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. Fixed |
||
|
|
||
| def handle_fn_equals(self, intrinsic_value, ignore_errors): | ||
| """ | ||
|
|
||
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.
[BUG] The AWS::NoValue handling is only applied in the dictionary branch. The list branch of intrinsic_property_resolver (around line 196) is unchanged:
So a per-element
Fn::Ifthat selectsAWS::NoValue— a common CloudFormation pattern — now resolves to a list containing a literalNone:Before this PR the Fn::If raised while resolving the unselected AWS::NoValue branch, so with ignore_errors=True the whole Layers property was left raw. After this PR it resolves to
[{"Ref": "BaseLayer"}, None]. CloudFormation removes the element from the list, so the resolved value should be a single-element list.Concrete consequences of the None element:
layer "None" is not recognizable, it might be using intrinsic functions that we don't support yet. Skipping.— misleading, since nothing unsupported was used.Architectures: [!If [UseArm, arm64, !Ref "AWS::NoValue"]], Function.architectures becomes[None], and validate_architecture_runtime (samcli/lib/utils/architecture.py:71) raisesUnsupportedRuntimeArchitectureError: Runtime ... is not supported on 'None' architecture. _get_function_architecture (samcli/lib/build/utils.py:51) returns None instead of defaulting to X86_64.Applying the same rule the new comment states ("CloudFormation drops such properties entirely") to lists keeps the two branches consistent:
This is safe: a literal null element in a template never reaches the filter, because intrinsic_property_resolver raises InvalidIntrinsicException on None input and the list comprehension has no try/except, so None elements can only originate from AWS::NoValue.