Skip to content

Commit b4ba802

Browse files
authored
Backwards compatibility format comparing (#3940)
* Backwards compatibility format comparing
1 parent 64ba1ac commit b4ba802

File tree

5 files changed

+43
-22
lines changed

5 files changed

+43
-22
lines changed

src/cfnlint/rules/formats/_schema_comparer.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77

88
from __future__ import annotations
99

10+
import logging
1011
from typing import Any, Callable
1112

1213
from cfnlint.jsonschema.exceptions import ValidationError
1314

1415
_keyword = "format"
1516

17+
LOGGER = logging.getLogger(__name__)
18+
1619

1720
def _any_of(
1821
source: str,
@@ -45,6 +48,15 @@ def _any_of(
4548
}
4649

4750

51+
def _backwards_compatibility(format: Any) -> Any:
52+
if format == "AWS::EC2::SecurityGroup.GroupId":
53+
LOGGER.warning(
54+
f"{format!r} is deprecated. Use 'AWS::EC2::SecurityGroup.Id' instead"
55+
)
56+
return "AWS::EC2::SecurityGroup.Id"
57+
return format
58+
59+
4860
def _compare_schemas(
4961
source: str,
5062
destination: dict[str, Any],
@@ -59,7 +71,7 @@ def _compare_schemas(
5971
tuple[bool, list[str]]: True/False if the format keyword matches
6072
"""
6173

62-
dest_f = destination.get(_keyword)
74+
dest_f = _backwards_compatibility(destination.get(_keyword))
6375
if dest_f == source:
6476
return True, [dest_f] # Nothing else matters to be on the safe side
6577

@@ -90,7 +102,8 @@ def compare_schemas(
90102
ValidationError: A ValidationError if the schemas don't match, otherwise None.
91103
"""
92104

93-
f = source.get(_keyword)
105+
f = _backwards_compatibility(source.get(_keyword))
106+
94107
if f is None:
95108
return None
96109

src/cfnlint/rules/functions/GetAttFormat.py

+19-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
from typing import Any
77

8-
from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
8+
from cfnlint.jsonschema import ValidationResult, Validator
9+
from cfnlint.jsonschema._utils import Unset
10+
from cfnlint.rules.formats._schema_comparer import compare_schemas
911
from cfnlint.rules.jsonschema import CfnLintKeyword
1012
from cfnlint.schema import PROVIDER_SCHEMA_MANAGER
1113

@@ -62,22 +64,22 @@ def validate(
6264
getatt_ptr = validator.context.resources[resource].get_atts(region)[attr]
6365

6466
getatt_schema = resource_schema.resolver.resolve_cfn_pointer(getatt_ptr)
65-
getatt_fmt = getatt_schema.get("format")
66-
if getatt_fmt != fmt:
67-
if getatt_fmt is None:
68-
yield ValidationError(
69-
(
70-
f"{{'Fn::GetAtt': {instance!r}}} does not match "
71-
f"destination format of {fmt!r}"
72-
),
73-
rule=self,
67+
68+
err = compare_schemas(schema, getatt_schema)
69+
if err:
70+
if err.instance:
71+
err.message = (
72+
f"{{'Fn::GetAtt': {instance!r}}} with formats {err.instance!r} "
73+
"does not match destination format of "
74+
f"{err.schema.get('format')!r}"
7475
)
7576
else:
76-
yield ValidationError(
77-
(
78-
f"{{'Fn::GetAtt': {instance!r}}} with format "
79-
f"{getatt_fmt!r} does not "
80-
f"match destination format of {fmt!r}"
81-
),
82-
rule=self,
77+
err.message = (
78+
f"{{'Fn::GetAtt': {instance!r}}} does not match "
79+
f"destination format of {err.schema.get('format')!r}"
8380
)
81+
82+
err.instance = Unset()
83+
err.schema = Unset()
84+
err.rule = self
85+
yield err

test/fixtures/results/integration/formats.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
},
3434
{
3535
"Filename": "test/fixtures/templates/integration/formats.yaml",
36-
"Id": "f76fa81f-837e-7502-0be7-3d8ff34024e1",
36+
"Id": "59600b0e-7e76-75f0-2c77-abc7279e2d4b",
3737
"Level": "Error",
3838
"Location": {
3939
"End": {
@@ -54,7 +54,7 @@
5454
"LineNumber": 44
5555
}
5656
},
57-
"Message": "{'Fn::GetAtt': ['SecurityGroup', 'GroupName']} with format 'AWS::EC2::SecurityGroup.Name' does not match destination format of 'AWS::EC2::SecurityGroup.Id'",
57+
"Message": "{'Fn::GetAtt': ['SecurityGroup', 'GroupName']} with formats ['AWS::EC2::SecurityGroup.Name'] does not match destination format of 'AWS::EC2::SecurityGroup.Id'",
5858
"ParentId": null,
5959
"Rule": {
6060
"Description": "Validate that if source and destination format exists that they match",

test/unit/rules/formats/test_schema_comparer.py

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
"'foo' format is incompatible",
3535
),
3636
),
37+
(
38+
"backwards compatibility",
39+
{"format": "AWS::EC2::SecurityGroup.GroupId"},
40+
{"format": "AWS::EC2::SecurityGroup.Id"},
41+
None,
42+
),
3743
(
3844
"basic valid with anyOf source",
3945
{"anyOf": [{"format": "foo"}, {"format": "bar"}]},

test/unit/rules/functions/test_getatt_format.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def template():
9494
ValidationError(
9595
(
9696
"{'Fn::GetAtt': ['MyBucket', 'WebsiteURL']} "
97-
"with format 'uri' does not match "
97+
"with formats ['uri'] does not match "
9898
"destination format of 'AWS::EC2::VPC.Id'"
9999
),
100100
rule=GetAttFormat(),

0 commit comments

Comments
 (0)