Skip to content

Commit

Permalink
Update getatt resolution for better regional support
Browse files Browse the repository at this point in the history
  • Loading branch information
kddejong committed Aug 13, 2024
1 parent 69fbdff commit d11cc46
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 60 deletions.
1 change: 0 additions & 1 deletion src/cfnlint/context/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,6 @@ def __post_init__(self, resource) -> None:
raise ValueError("Condition must be a string")
self.condition = c

@property
def get_atts(self, region: str = "us-east-1") -> AttributeDict:
return PROVIDER_SCHEMA_MANAGER.get_type_getatts(self.type, region)

Expand Down
82 changes: 44 additions & 38 deletions src/cfnlint/rules/functions/GetAtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from cfnlint.helpers import ensure_list, is_types_compatible
from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
from cfnlint.rules.functions._BaseFn import BaseFn, all_types
from cfnlint.schema import PROVIDER_SCHEMA_MANAGER
from cfnlint.schema import PROVIDER_SCHEMA_MANAGER, ResourceNotFoundError


class GetAtt(BaseFn):
Expand Down Expand Up @@ -82,43 +82,49 @@ def _resolve_getatt(
yield err
break
else:
for attribute_name, _, _ in validator.resolve_value(value[1]):
if all(
not (bool(re.fullmatch(each, attribute_name)))
for each in validator.context.resources[resource_name].get_atts
):
err = ValidationError(
(
f"{attribute_name!r} is not one of "
f"{validator.context.resources[resource_name].get_atts!r}"
),
validator=self.fn.py,
path=deque([self.fn.name, 1]),
)
if attribute_name != value[1]:
err.message = (
err.message + f" when {value[1]!r} is resolved"
)
yield err
continue

evolved = validator.evolve(schema=s) # type: ignore
evolved.validators = { # type: ignore
"type": validator.validators.get("type"), # type: ignore
}

getatts = validator.cfn.get_valid_getatts()
t = validator.context.resources[resource_name].type
pointer = getatts.match(
validator.context.regions[0], [resource_name, attribute_name]
)

for (
_,
schema,
) in PROVIDER_SCHEMA_MANAGER.get_resource_schemas_by_regions(
t, validator.context.regions
):
t = validator.context.resources[resource_name].type
for (
regions,
schema,
) in PROVIDER_SCHEMA_MANAGER.get_resource_schemas_by_regions(
t, validator.context.regions
):
region = regions[0]
for attribute_name, _, _ in validator.resolve_value(value[1]):
try:
if all(
not (bool(re.fullmatch(each, attribute_name)))
for each in validator.context.resources[
resource_name
].get_atts(regions[0])
):
err = ValidationError(
(
f"{attribute_name!r} is not one of "
f"{validator.context.resources[resource_name].get_atts(region)!r}"
f" in {regions!r}"
),
validator=self.fn.py,
path=deque([self.fn.name, 1]),
)
if attribute_name != value[1]:
err.message = (
err.message + f" when {value[1]!r} is resolved"
)
yield err
continue
except ResourceNotFoundError:
continue

Check warning on line 117 in src/cfnlint/rules/functions/GetAtt.py

View check run for this annotation

Codecov / codecov/patch

src/cfnlint/rules/functions/GetAtt.py#L116-L117

Added lines #L116 - L117 were not covered by tests

evolved = validator.evolve(schema=s) # type: ignore
evolved.validators = { # type: ignore
"type": validator.validators.get("type"), # type: ignore
}

getatts = validator.cfn.get_valid_getatts()
t = validator.context.resources[resource_name].type
pointer = getatts.match(region, [resource_name, attribute_name])

getatt_schema = schema.resolver.resolve_cfn_pointer(pointer)
if not getatt_schema.get("type") or not s.get("type"):
continue
Expand Down
41 changes: 23 additions & 18 deletions src/cfnlint/rules/functions/GetAttFormat.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
from cfnlint.rules.jsonschema import CfnLintKeyword
from cfnlint.schema import PROVIDER_SCHEMA_MANAGER
from cfnlint.schema import PROVIDER_SCHEMA_MANAGER, ResourceNotFoundError


class GetAttFormat(CfnLintKeyword):
Expand Down Expand Up @@ -42,22 +42,27 @@ def validate(

resource, attr = instance[0:2]

getatt_ptr = validator.context.resources[resource].get_atts[attr]
t = validator.context.resources[resource].type
try:
getatt_ptr = validator.context.resources[resource].get_atts(
validator.context.regions[0]
)[attr]
t = validator.context.resources[resource].type

if t in self._resource_type_exceptions:
return
if t in self._resource_type_exceptions:
return

for (
_,
resource_schema,
) in PROVIDER_SCHEMA_MANAGER.get_resource_schemas_by_regions(
t, validator.context.regions
):
getatt_schema = resource_schema.resolver.resolve_cfn_pointer(getatt_ptr)
getatt_fmt = getatt_schema.get("format")
if getatt_fmt != fmt:
yield ValidationError(
f"{{'Fn::GetAtt': {instance!r}}} that does not match {fmt!r}",
rule=self,
)
for (
_,
resource_schema,
) in PROVIDER_SCHEMA_MANAGER.get_resource_schemas_by_regions(
t, validator.context.regions
):
getatt_schema = resource_schema.resolver.resolve_cfn_pointer(getatt_ptr)
getatt_fmt = getatt_schema.get("format")
if getatt_fmt != fmt:
yield ValidationError(
f"{{'Fn::GetAtt': {instance!r}}} that does not match {fmt!r}",
rule=self,
)
except ResourceNotFoundError:
pass

Check warning on line 68 in src/cfnlint/rules/functions/GetAttFormat.py

View check run for this annotation

Codecov / codecov/patch

src/cfnlint/rules/functions/GetAttFormat.py#L67-L68

Added lines #L67 - L68 were not covered by tests
5 changes: 3 additions & 2 deletions test/unit/rules/functions/test_getatt.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ def validate(self, validator, s, instance, schema):
ValidationError(
(
"'foo' is not one of ['Arn', 'DomainName', "
"'DualStackDomainName', 'RegionalDomainName', 'WebsiteURL']"
"'DualStackDomainName', 'RegionalDomainName', 'WebsiteURL'] "
"in ['us-east-1']"
),
path=deque(["Fn::GetAtt", 1]),
schema_path=deque([]),
Expand Down Expand Up @@ -219,7 +220,7 @@ def validate(self, validator, s, instance, schema):
(
"'MyBucket' is not one of ['Arn', 'DomainName', "
"'DualStackDomainName', 'RegionalDomainName', "
"'WebsiteURL'] when "
"'WebsiteURL'] in ['us-east-1'] when "
"{'Ref': 'MyResourceParameter'} is resolved"
),
path=deque(["Fn::GetAtt", 1]),
Expand Down
2 changes: 1 addition & 1 deletion test/unit/rules/functions/test_sub.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def context(cfn):
"'DomainName', "
"'DualStackDomainName', "
"'RegionalDomainName', "
"'WebsiteURL']"
"'WebsiteURL'] in ['us-east-1']"
),
path=deque(["Fn::Sub"]),
schema_path=deque([]),
Expand Down

0 comments on commit d11cc46

Please sign in to comment.