Skip to content

Commit

Permalink
Support Ignore Globals resource attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
GavinZZ committed Oct 20, 2023
1 parent 4fac468 commit 1214037
Show file tree
Hide file tree
Showing 14 changed files with 920 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ class Properties(BaseModel):
SnapStart: Optional[SnapStart] = prop("SnapStart")
RuntimeManagementConfig: Optional[RuntimeManagementConfig] = prop("RuntimeManagementConfig")
Tags: Optional[Tags] = prop("Tags")
PropagateTags: Optional[bool] # TODO: add docs
PropagateTags: Optional[bool] = prop("PropagateTags")
Timeout: Optional[Timeout] = prop("Timeout")
Tracing: Optional[Tracing] = prop("Tracing")
VersionDescription: Optional[PassThroughProp] = prop("VersionDescription")
Expand Down Expand Up @@ -656,7 +656,7 @@ class Globals(BaseModel):
["AWS::Lambda::Function", "Properties", "Environment"],
)
Tags: Optional[Tags] = prop("Tags")
PropagateTags: Optional[bool] # TODO: add docs
PropagateTags: Optional[bool] = prop("PropagateTags")
Tracing: Optional[Tracing] = prop("Tracing")
KmsKeyArn: Optional[KmsKeyArn] = prop("KmsKeyArn")
Layers: Optional[Layers] = prop("Layers")
Expand Down
1 change: 1 addition & 0 deletions samtranslator/internal/schema_source/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,4 @@ class ResourceAttributes(BaseModel):
Metadata: Optional[PassThroughProp]
UpdateReplacePolicy: Optional[PassThroughProp]
Condition: Optional[PassThroughProp]
IgnoreGlobals: Optional[Union[str, List[str]]]
62 changes: 54 additions & 8 deletions samtranslator/plugins/globals/globals.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any, Dict, List
import copy
from typing import Any, Dict, List, Optional, Union

from samtranslator.model.exceptions import ExceptionWithMessage
from samtranslator.model.exceptions import ExceptionWithMessage, InvalidResourceAttributeTypeException
from samtranslator.public.intrinsics import is_intrinsics
from samtranslator.public.sdk.resource import SamResourceType
from samtranslator.swagger.swagger import SwaggerEditor
Expand Down Expand Up @@ -99,7 +100,7 @@ class Globals:
SamResourceType.Function.value: ["RuntimeManagementConfig"],
}

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

self.template_globals = {}
self.template_globals: Dict[str, GlobalProperties] = {}

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

def merge(self, resource_type, resource_properties): # type: ignore[no-untyped-def]
def get_template_globals(
self, logical_id: str, resource_type: str, ignore_globals: Optional[Union[str, List[str]]]
) -> "GlobalProperties":
"""
Get template globals but remove globals based on IgnoreGlobals attribute.
:param string logical_id: LogicalId of the resource
:param string resource_type: Type of the resource (Ex: AWS::Serverless::Function)
:param dict ignore_globals: IgnoreGlobals resource attribute. It can be either 1) "*" string value
or list of string value, each value should be a valid property in Globals section
:return dict: processed template globals
"""
if not ignore_globals:
return self.template_globals[resource_type]

if isinstance(ignore_globals, str) and ignore_globals == "*":
return GlobalProperties({})

if isinstance(ignore_globals, list):
global_props: GlobalProperties = copy.deepcopy(self.template_globals[resource_type])
for key in ignore_globals:
if key not in global_props.global_properties:
raise InvalidResourceAttributeTypeException(
logical_id,
"IgnoreGlobals",
None,
f"Resource {logical_id} has invalid resource attribute 'IgnoreGlobals' on item '{key}'.",
)
del global_props.global_properties[key]
return global_props

# We raise exception for any non "*" or non-list input
raise InvalidResourceAttributeTypeException(
logical_id,
"IgnoreGlobals",
None,
f"Resource {logical_id} has invalid resource attribute 'IgnoreGlobals'.",
)

def merge(
self,
resource_type: str,
resource_properties: Dict[str, Any],
logical_id: str = "",
ignore_globals: Optional[Union[str, List[str]]] = None,
) -> Any:
"""
Adds global properties to the resource, if necessary. This method is a no-op if there are no global properties
for this resource type
Expand All @@ -130,12 +176,12 @@ def merge(self, resource_type, resource_properties): # type: ignore[no-untyped-
# Nothing to do. Return the template unmodified
return resource_properties

global_props = self.template_globals[resource_type]
global_props = self.get_template_globals(logical_id, str(resource_type), ignore_globals)

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

@classmethod
def del_section(cls, template): # type: ignore[no-untyped-def]
def del_section(cls, template: Dict[str, Any]) -> None:
"""
Helper method to delete the Globals section altogether from the template
Expand Down
14 changes: 11 additions & 3 deletions samtranslator/plugins/globals/globals_plugin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from typing import Any, Dict

from samtranslator.metrics.method_decorator import cw_timer
from samtranslator.model.exceptions import InvalidResourceAttributeTypeException
from samtranslator.plugins.globals.globals import Globals, InvalidGlobalsSectionException
from samtranslator.public.exceptions import InvalidDocumentException
from samtranslator.public.plugins import BasePlugin
Expand All @@ -13,7 +16,7 @@ class GlobalsPlugin(BasePlugin):
"""

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

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

# If there was a global openApiVersion flag, check and convert swagger
# to the right version
Expand Down
132 changes: 130 additions & 2 deletions samtranslator/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -249780,6 +249780,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -252731,6 +252745,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -253032,6 +253060,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -253116,6 +253158,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -253613,7 +253669,8 @@
"type": "string"
},
"PropagateTags": {
"title": "Propagatetags",
"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\\.",
"title": "PropagateTags",
"type": "boolean"
},
"ProvisionedConcurrencyConfig": {
Expand Down Expand Up @@ -253992,7 +254049,8 @@
"title": "Policies"
},
"PropagateTags": {
"title": "Propagatetags",
"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\\.",
"title": "PropagateTags",
"type": "boolean"
},
"ProvisionedConcurrencyConfig": {
Expand Down Expand Up @@ -254121,6 +254179,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -255040,6 +255112,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -255198,6 +255284,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -255292,6 +255392,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down Expand Up @@ -255839,6 +255953,20 @@
"DependsOn": {
"$ref": "#/definitions/PassThroughProp"
},
"IgnoreGlobals": {
"anyOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"title": "Ignoreglobals"
},
"Metadata": {
"$ref": "#/definitions/PassThroughProp"
},
Expand Down
3 changes: 2 additions & 1 deletion samtranslator/sdk/resource.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from enum import Enum
from typing import Any, Dict
from typing import Any, Dict, List, Optional, Union

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

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

0 comments on commit 1214037

Please sign in to comment.