Skip to content

Commit a2be62d

Browse files
authored
feat: Support Ignore Globals resource attribute (#3386)
1 parent e12bca5 commit a2be62d

14 files changed

+920
-18
lines changed

samtranslator/internal/schema_source/aws_serverless_function.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ class Properties(BaseModel):
626626
SnapStart: Optional[SnapStart] = prop("SnapStart")
627627
RuntimeManagementConfig: Optional[RuntimeManagementConfig] = prop("RuntimeManagementConfig")
628628
Tags: Optional[Tags] = prop("Tags")
629-
PropagateTags: Optional[bool] # TODO: add docs
629+
PropagateTags: Optional[bool] = prop("PropagateTags")
630630
Timeout: Optional[Timeout] = prop("Timeout")
631631
Tracing: Optional[Tracing] = prop("Tracing")
632632
VersionDescription: Optional[PassThroughProp] = prop("VersionDescription")
@@ -656,7 +656,7 @@ class Globals(BaseModel):
656656
["AWS::Lambda::Function", "Properties", "Environment"],
657657
)
658658
Tags: Optional[Tags] = prop("Tags")
659-
PropagateTags: Optional[bool] # TODO: add docs
659+
PropagateTags: Optional[bool] = prop("PropagateTags")
660660
Tracing: Optional[Tracing] = prop("Tracing")
661661
KmsKeyArn: Optional[KmsKeyArn] = prop("KmsKeyArn")
662662
Layers: Optional[Layers] = prop("Layers")

samtranslator/internal/schema_source/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,4 @@ class ResourceAttributes(BaseModel):
9999
Metadata: Optional[PassThroughProp]
100100
UpdateReplacePolicy: Optional[PassThroughProp]
101101
Condition: Optional[PassThroughProp]
102+
IgnoreGlobals: Optional[Union[str, List[str]]]

samtranslator/plugins/globals/globals.py

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
from typing import Any, Dict, List
1+
import copy
2+
from typing import Any, Dict, List, Optional, Union
23

3-
from samtranslator.model.exceptions import ExceptionWithMessage
4+
from samtranslator.model.exceptions import ExceptionWithMessage, InvalidResourceAttributeTypeException
45
from samtranslator.public.intrinsics import is_intrinsics
56
from samtranslator.public.sdk.resource import SamResourceType
67
from samtranslator.swagger.swagger import SwaggerEditor
@@ -99,7 +100,7 @@ class Globals:
99100
SamResourceType.Function.value: ["RuntimeManagementConfig"],
100101
}
101102

102-
def __init__(self, template) -> None: # type: ignore[no-untyped-def]
103+
def __init__(self, template: Dict[str, Any]) -> None:
103104
"""
104105
Constructs an instance of this object
105106
@@ -111,12 +112,57 @@ def __init__(self, template) -> None: # type: ignore[no-untyped-def]
111112
# Sort the names for stability in list ordering
112113
self.supported_resource_section_names.sort()
113114

114-
self.template_globals = {}
115+
self.template_globals: Dict[str, GlobalProperties] = {}
115116

116117
if self._KEYWORD in template:
117118
self.template_globals = self._parse(template[self._KEYWORD]) # type: ignore[no-untyped-call]
118119

119-
def merge(self, resource_type, resource_properties): # type: ignore[no-untyped-def]
120+
def get_template_globals(
121+
self, logical_id: str, resource_type: str, ignore_globals: Optional[Union[str, List[str]]]
122+
) -> "GlobalProperties":
123+
"""
124+
Get template globals but remove globals based on IgnoreGlobals attribute.
125+
126+
:param string logical_id: LogicalId of the resource
127+
:param string resource_type: Type of the resource (Ex: AWS::Serverless::Function)
128+
:param dict ignore_globals: IgnoreGlobals resource attribute. It can be either 1) "*" string value
129+
or list of string value, each value should be a valid property in Globals section
130+
:return dict: processed template globals
131+
"""
132+
if not ignore_globals:
133+
return self.template_globals[resource_type]
134+
135+
if isinstance(ignore_globals, str) and ignore_globals == "*":
136+
return GlobalProperties({})
137+
138+
if isinstance(ignore_globals, list):
139+
global_props: GlobalProperties = copy.deepcopy(self.template_globals[resource_type])
140+
for key in ignore_globals:
141+
if key not in global_props.global_properties:
142+
raise InvalidResourceAttributeTypeException(
143+
logical_id,
144+
"IgnoreGlobals",
145+
None,
146+
f"Resource {logical_id} has invalid resource attribute 'IgnoreGlobals' on item '{key}'.",
147+
)
148+
del global_props.global_properties[key]
149+
return global_props
150+
151+
# We raise exception for any non "*" or non-list input
152+
raise InvalidResourceAttributeTypeException(
153+
logical_id,
154+
"IgnoreGlobals",
155+
None,
156+
f"Resource {logical_id} has invalid resource attribute 'IgnoreGlobals'.",
157+
)
158+
159+
def merge(
160+
self,
161+
resource_type: str,
162+
resource_properties: Dict[str, Any],
163+
logical_id: str = "",
164+
ignore_globals: Optional[Union[str, List[str]]] = None,
165+
) -> Any:
120166
"""
121167
Adds global properties to the resource, if necessary. This method is a no-op if there are no global properties
122168
for this resource type
@@ -130,12 +176,12 @@ def merge(self, resource_type, resource_properties): # type: ignore[no-untyped-
130176
# Nothing to do. Return the template unmodified
131177
return resource_properties
132178

133-
global_props = self.template_globals[resource_type]
179+
global_props = self.get_template_globals(logical_id, str(resource_type), ignore_globals)
134180

135-
return global_props.merge(resource_properties)
181+
return global_props.merge(resource_properties) # type: ignore[no-untyped-call]
136182

137183
@classmethod
138-
def del_section(cls, template): # type: ignore[no-untyped-def]
184+
def del_section(cls, template: Dict[str, Any]) -> None:
139185
"""
140186
Helper method to delete the Globals section altogether from the template
141187

samtranslator/plugins/globals/globals_plugin.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
from typing import Any, Dict
2+
13
from samtranslator.metrics.method_decorator import cw_timer
4+
from samtranslator.model.exceptions import InvalidResourceAttributeTypeException
25
from samtranslator.plugins.globals.globals import Globals, InvalidGlobalsSectionException
36
from samtranslator.public.exceptions import InvalidDocumentException
47
from samtranslator.public.plugins import BasePlugin
@@ -13,7 +16,7 @@ class GlobalsPlugin(BasePlugin):
1316
"""
1417

1518
@cw_timer(prefix="Plugin-Globals")
16-
def on_before_transform_template(self, template_dict): # type: ignore[no-untyped-def]
19+
def on_before_transform_template(self, template_dict: Dict[str, Any]) -> None:
1720
"""
1821
Hook method that runs before a template gets transformed. In this method, we parse and process Globals section
1922
from the template (if present).
@@ -28,11 +31,16 @@ def on_before_transform_template(self, template_dict): # type: ignore[no-untype
2831
# For each resource in template, try and merge with Globals if necessary
2932
template = SamTemplate(template_dict)
3033
for logicalId, resource in template.iterate():
31-
resource.properties = global_section.merge(resource.type, resource.properties) # type: ignore[no-untyped-call]
34+
try:
35+
resource.properties = global_section.merge(
36+
str(resource.type), resource.properties, logicalId, resource.ignore_globals
37+
)
38+
except InvalidResourceAttributeTypeException as ex:
39+
raise InvalidDocumentException([ex]) from ex
3240
template.set(logicalId, resource)
3341

3442
# Remove the Globals section from template if necessary
35-
Globals.del_section(template_dict) # type: ignore[no-untyped-call]
43+
Globals.del_section(template_dict)
3644

3745
# If there was a global openApiVersion flag, check and convert swagger
3846
# to the right version

samtranslator/schema/schema.json

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250245,6 +250245,20 @@
250245250245
"DependsOn": {
250246250246
"$ref": "#/definitions/PassThroughProp"
250247250247
},
250248+
"IgnoreGlobals": {
250249+
"anyOf": [
250250+
{
250251+
"type": "string"
250252+
},
250253+
{
250254+
"items": {
250255+
"type": "string"
250256+
},
250257+
"type": "array"
250258+
}
250259+
],
250260+
"title": "Ignoreglobals"
250261+
},
250248250262
"Metadata": {
250249250263
"$ref": "#/definitions/PassThroughProp"
250250250264
},
@@ -253196,6 +253210,20 @@
253196253210
"DependsOn": {
253197253211
"$ref": "#/definitions/PassThroughProp"
253198253212
},
253213+
"IgnoreGlobals": {
253214+
"anyOf": [
253215+
{
253216+
"type": "string"
253217+
},
253218+
{
253219+
"items": {
253220+
"type": "string"
253221+
},
253222+
"type": "array"
253223+
}
253224+
],
253225+
"title": "Ignoreglobals"
253226+
},
253199253227
"Metadata": {
253200253228
"$ref": "#/definitions/PassThroughProp"
253201253229
},
@@ -253497,6 +253525,20 @@
253497253525
"DependsOn": {
253498253526
"$ref": "#/definitions/PassThroughProp"
253499253527
},
253528+
"IgnoreGlobals": {
253529+
"anyOf": [
253530+
{
253531+
"type": "string"
253532+
},
253533+
{
253534+
"items": {
253535+
"type": "string"
253536+
},
253537+
"type": "array"
253538+
}
253539+
],
253540+
"title": "Ignoreglobals"
253541+
},
253500253542
"Metadata": {
253501253543
"$ref": "#/definitions/PassThroughProp"
253502253544
},
@@ -253581,6 +253623,20 @@
253581253623
"DependsOn": {
253582253624
"$ref": "#/definitions/PassThroughProp"
253583253625
},
253626+
"IgnoreGlobals": {
253627+
"anyOf": [
253628+
{
253629+
"type": "string"
253630+
},
253631+
{
253632+
"items": {
253633+
"type": "string"
253634+
},
253635+
"type": "array"
253636+
}
253637+
],
253638+
"title": "Ignoreglobals"
253639+
},
253584253640
"Metadata": {
253585253641
"$ref": "#/definitions/PassThroughProp"
253586253642
},
@@ -254078,7 +254134,8 @@
254078254134
"type": "string"
254079254135
},
254080254136
"PropagateTags": {
254081-
"title": "Propagatetags",
254137+
"markdownDescription": "Indicate whether or not to pass tags from the `Tags` property to your [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html) generated resources\\. Specify `True` to propagate tags in your generated resources\\. \n*Type*: Boolean \n*Required*: No \n*Default*: `False` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
254138+
"title": "PropagateTags",
254082254139
"type": "boolean"
254083254140
},
254084254141
"ProvisionedConcurrencyConfig": {
@@ -254457,7 +254514,8 @@
254457254514
"title": "Policies"
254458254515
},
254459254516
"PropagateTags": {
254460-
"title": "Propagatetags",
254517+
"markdownDescription": "Indicate whether or not to pass tags from the `Tags` property to your [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html) generated resources\\. Specify `True` to propagate tags in your generated resources\\. \n*Type*: Boolean \n*Required*: No \n*Default*: `False` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
254518+
"title": "PropagateTags",
254461254519
"type": "boolean"
254462254520
},
254463254521
"ProvisionedConcurrencyConfig": {
@@ -254586,6 +254644,20 @@
254586254644
"DependsOn": {
254587254645
"$ref": "#/definitions/PassThroughProp"
254588254646
},
254647+
"IgnoreGlobals": {
254648+
"anyOf": [
254649+
{
254650+
"type": "string"
254651+
},
254652+
{
254653+
"items": {
254654+
"type": "string"
254655+
},
254656+
"type": "array"
254657+
}
254658+
],
254659+
"title": "Ignoreglobals"
254660+
},
254589254661
"Metadata": {
254590254662
"$ref": "#/definitions/PassThroughProp"
254591254663
},
@@ -255505,6 +255577,20 @@
255505255577
"DependsOn": {
255506255578
"$ref": "#/definitions/PassThroughProp"
255507255579
},
255580+
"IgnoreGlobals": {
255581+
"anyOf": [
255582+
{
255583+
"type": "string"
255584+
},
255585+
{
255586+
"items": {
255587+
"type": "string"
255588+
},
255589+
"type": "array"
255590+
}
255591+
],
255592+
"title": "Ignoreglobals"
255593+
},
255508255594
"Metadata": {
255509255595
"$ref": "#/definitions/PassThroughProp"
255510255596
},
@@ -255663,6 +255749,20 @@
255663255749
"DependsOn": {
255664255750
"$ref": "#/definitions/PassThroughProp"
255665255751
},
255752+
"IgnoreGlobals": {
255753+
"anyOf": [
255754+
{
255755+
"type": "string"
255756+
},
255757+
{
255758+
"items": {
255759+
"type": "string"
255760+
},
255761+
"type": "array"
255762+
}
255763+
],
255764+
"title": "Ignoreglobals"
255765+
},
255666255766
"Metadata": {
255667255767
"$ref": "#/definitions/PassThroughProp"
255668255768
},
@@ -255757,6 +255857,20 @@
255757255857
"DependsOn": {
255758255858
"$ref": "#/definitions/PassThroughProp"
255759255859
},
255860+
"IgnoreGlobals": {
255861+
"anyOf": [
255862+
{
255863+
"type": "string"
255864+
},
255865+
{
255866+
"items": {
255867+
"type": "string"
255868+
},
255869+
"type": "array"
255870+
}
255871+
],
255872+
"title": "Ignoreglobals"
255873+
},
255760255874
"Metadata": {
255761255875
"$ref": "#/definitions/PassThroughProp"
255762255876
},
@@ -256304,6 +256418,20 @@
256304256418
"DependsOn": {
256305256419
"$ref": "#/definitions/PassThroughProp"
256306256420
},
256421+
"IgnoreGlobals": {
256422+
"anyOf": [
256423+
{
256424+
"type": "string"
256425+
},
256426+
{
256427+
"items": {
256428+
"type": "string"
256429+
},
256430+
"type": "array"
256431+
}
256432+
],
256433+
"title": "Ignoreglobals"
256434+
},
256307256435
"Metadata": {
256308256436
"$ref": "#/definitions/PassThroughProp"
256309256437
},

samtranslator/sdk/resource.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from enum import Enum
2-
from typing import Any, Dict
2+
from typing import Any, Dict, List, Optional, Union
33

44
from samtranslator.model.exceptions import InvalidDocumentException, InvalidTemplateException
55
from samtranslator.model.types import IS_STR
@@ -27,6 +27,7 @@ def __init__(self, resource_dict: Dict[str, Any]) -> None:
2727
self.condition = resource_dict.get("Condition", None)
2828
self.deletion_policy = resource_dict.get("DeletionPolicy", None)
2929
self.update_replace_policy = resource_dict.get("UpdateReplacePolicy", None)
30+
self.ignore_globals: Optional[Union[str, List[str]]] = resource_dict.get("IgnoreGlobals", None)
3031

3132
# Properties is *not* required. Ex: SimpleTable resource has no required properties
3233
self.properties = resource_dict.get("Properties", {})

0 commit comments

Comments
 (0)