-
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 4 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 |
|---|---|---|
|
|
@@ -615,7 +615,7 @@ def handle_fn_getatt(self, intrinsic_value, ignore_errors): | |
| verify_intrinsic_type_str(logical_id, IntrinsicResolver.FN_GET_ATT) | ||
| verify_intrinsic_type_str(resource_type, IntrinsicResolver.FN_GET_ATT) | ||
|
|
||
| return self._symbol_resolver.resolve_symbols(logical_id, resource_type) | ||
| return self._symbol_resolver.resolve_symbols(logical_id, resource_type, ignore_errors) | ||
|
|
||
| def handle_fn_ref(self, intrinsic_value, ignore_errors): | ||
| """ | ||
|
|
@@ -716,24 +716,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 +740,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] Forwarding
ignore_errorsintoresolve_symbolschanges behavior for every unresolvableFn::GetAttin the template, not just ones inside a selectedFn::Ifbranch, and it regresses nested-stack layer handling.SamBaseProvider.get_template/get_resolved_template_dictboth callresolver.resolve_template(ignore_errors=True), so this is the only mode sam build and sam local ever run in. Previously an unresolvable GetAtt raisedInvalidSymbolException, which the dict-recursion handler inintrinsic_property_resolver(andresolve_attribute) caught and used to leave the property as the raw intrinsic dict. Now it silently becomes the string"$LogicalId.Attribute", and downstream code that distinguishes "unresolved intrinsic dict" from "resolved string" takes the wrong path.Concretely, for a layer referencing a nested stack output — the exact shape
nested_stack_manager.pyemits and thatSamFunctionProvider._locate_layer_from_nesteddocuments:resolve_symbols("AwsSamAutoDependencyLayerNestedStack", "Outputs.MyDepLayer")has no match inlogical_id_translator, parameters,default_type_resolver, orcommon_attribute_resolver(which only handlesRefandArn). Before this change the entry stayed a dict and_parse_layer_infofell into itselsebranch, logging layer "..." is not recognizable ... Skipping. After this change the entry is the string"$AwsSamAutoDependencyLayerNestedStack.Outputs.MyDepLayer", soisinstance(layer, str)is true,locate_layer_nestedisFalsefor build and local invoke, and it is passed toLayerVersion(layer, None, ...)._compute_layer_name/_compute_layer_versionthen fail torsplitan ARN out of it and raiseInvalidLayerVersionArn— a hard failure where the layer used to be skipped.This change also isn't required for the
Fn::Iffix. If the selected branch contains an unsupportedFn::GetAtt, the exception already propagates out ofhandle_fn_ifto the enclosing dict recursion, which catches it underignore_errors=Trueand preserves the original value. Suggest reverting this line to keep the blast radius limited tohandle_fn_if:If the placeholder degradation is genuinely wanted, it needs to be scoped so it can't turn an unresolved layer reference into something
_parse_layer_infomistakes for a literal ARN, plus a test coveringLayers: [!GetAtt Stack.Outputs.Layer]. Note thattests/unit/.../test_intrinsic_resolver.py:471currently locks in the new placeholder behavior, so this would need updating too.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.
Fixed
88ffaea