Skip to content

Commit d87e63c

Browse files
authored
Update GetAtt logic for SC Cfn type (#3475)
1 parent 78e9fa9 commit d87e63c

File tree

3 files changed

+160
-6
lines changed

3 files changed

+160
-6
lines changed

src/cfnlint/rules/functions/GetAtt.py

-5
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ def _resolve_getatt(
102102
yield err
103103
continue
104104

105-
# because of the complexities of schemas ($ref, anyOf, allOf, etc.)
106-
# we will simplify the validator to just have a type check
107-
# then we will provide a simple value to represent the type from the
108-
# getAtt
109105
evolved = validator.evolve(schema=s) # type: ignore
110106
evolved.validators = { # type: ignore
111107
"type": validator.validators.get("type"), # type: ignore
@@ -124,7 +120,6 @@ def _resolve_getatt(
124120
t, validator.context.regions
125121
):
126122
getatt_schema = schema.resolver.resolve_cfn_pointer(pointer)
127-
128123
if not getatt_schema.get("type") or not s.get("type"):
129124
continue
130125

src/cfnlint/schema/_getatts.py

+8
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,14 @@ def __init__(self, schema: "Schema") -> None:
253253
self._attrs["Outputs\\..*"] = "/properties/CfnLintStringType"
254254
return
255255

256+
if schema.type_name == "AWS::ServiceCatalog::CloudFormationProvisionedProduct":
257+
for ro_attr in schema.schema.get("readOnlyProperties", []):
258+
if ro_attr == "/properties/Outputs":
259+
self._attrs["Outputs\\..*"] = "/properties/CfnLintStringType"
260+
else:
261+
self._attrs[self._pointer_to_attr(ro_attr)] = ro_attr
262+
return
263+
256264
for unnamed_type in _unnamed_unknown_types:
257265
if schema.type_name.startswith(unnamed_type):
258266
self._attrs[".*"] = "/properties/CfnLintAllTypes"

test/unit/module/schema/test_schema.py

+152-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ def resource_vpc_schema():
142142

143143

144144
def test_vpc_schema(resource_vpc_schema):
145-
146145
schema = Schema(schema=resource_vpc_schema)
147146
assert list(schema.get_atts.keys()) == [
148147
"CidrBlock",
@@ -161,3 +160,155 @@ def test_vpc_schema(resource_vpc_schema):
161160
assert schema.get_atts["VpcId"] == "/properties/VpcId"
162161
assert schema.get_atts["CidrBlock"] == "/properties/CidrBlock"
163162
assert schema.get_atts["Ipv6CidrBlocks"] == "/properties/Ipv6CidrBlocks"
163+
164+
165+
@pytest.fixture
166+
def resource_servicecatalog_schema():
167+
return {
168+
"additionalProperties": False,
169+
"createOnlyProperties": [
170+
"/properties/NotificationArns",
171+
"/properties/ProvisionedProductName",
172+
],
173+
"definitions": {
174+
"OutputType": {"type": "string"},
175+
"ProvisioningParameter": {
176+
"additionalProperties": False,
177+
"properties": {
178+
"Key": {"maxLength": 1000, "minLength": 1, "type": "string"},
179+
"Value": {"maxLength": 4096, "type": "string"},
180+
},
181+
"required": ["Key", "Value"],
182+
"type": "object",
183+
},
184+
"ProvisioningPreferences": {
185+
"additionalProperties": False,
186+
"properties": {
187+
"StackSetAccounts": {
188+
"items": {"pattern": "^[0-9]{12}$", "type": "string"},
189+
"type": "array",
190+
"uniqueItems": True,
191+
},
192+
"StackSetFailureToleranceCount": {"minimum": 0, "type": "integer"},
193+
"StackSetFailureTolerancePercentage": {
194+
"maximum": 100,
195+
"minimum": 0,
196+
"type": "integer",
197+
},
198+
"StackSetMaxConcurrencyCount": {"minimum": 1, "type": "integer"},
199+
"StackSetMaxConcurrencyPercentage": {
200+
"maximum": 100,
201+
"minimum": 1,
202+
"type": "integer",
203+
},
204+
"StackSetOperationType": {
205+
"enum": ["CREATE", "UPDATE", "DELETE"],
206+
"type": "string",
207+
},
208+
"StackSetRegions": {
209+
"items": {
210+
"pattern": "^[a-z]{2}-([a-z]+-)+[1-9]",
211+
"type": "string",
212+
},
213+
"type": "array",
214+
"uniqueItems": True,
215+
},
216+
},
217+
"type": "object",
218+
},
219+
"Tag": {
220+
"additionalProperties": False,
221+
"properties": {
222+
"Key": {
223+
"maxLength": 128,
224+
"minLength": 1,
225+
"pattern": "^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$",
226+
"type": "string",
227+
},
228+
"Value": {
229+
"maxLength": 256,
230+
"minLength": 1,
231+
"pattern": "^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$",
232+
"type": "string",
233+
},
234+
},
235+
"required": ["Key", "Value"],
236+
"type": "object",
237+
},
238+
},
239+
"documentationUrl": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-servicecatalog-cloudformationprovisionedproduct.html",
240+
"handlers": {
241+
"create": {"permissions": ["*"], "timeoutInMinutes": 720},
242+
"delete": {"permissions": ["*"]},
243+
"read": {"permissions": ["*"]},
244+
"update": {"permissions": ["*"], "timeoutInMinutes": 720},
245+
},
246+
"primaryIdentifier": ["/properties/ProvisionedProductId"],
247+
"properties": {
248+
"AcceptLanguage": {"enum": ["en", "jp", "zh"], "type": "string"},
249+
"CloudformationStackArn": {
250+
"maxLength": 256,
251+
"minLength": 1,
252+
"type": "string",
253+
},
254+
"NotificationArns": {
255+
"items": {"type": "string"},
256+
"maxItems": 5,
257+
"type": "array",
258+
"uniqueItems": True,
259+
},
260+
"Outputs": {
261+
"additionalProperties": False,
262+
"maxProperties": 100,
263+
"patternProperties": {
264+
"^[A-Za-z0-9]{1,64}$": {"$ref": "#/definitions/OutputType"}
265+
},
266+
"type": "object",
267+
},
268+
"PathId": {"maxLength": 100, "minLength": 1, "type": "string"},
269+
"PathName": {"maxLength": 100, "minLength": 1, "type": "string"},
270+
"ProductId": {"maxLength": 100, "minLength": 1, "type": "string"},
271+
"ProductName": {"maxLength": 128, "minLength": 1, "type": "string"},
272+
"ProvisionedProductId": {"maxLength": 50, "minLength": 1, "type": "string"},
273+
"ProvisionedProductName": {
274+
"maxLength": 128,
275+
"minLength": 1,
276+
"type": "string",
277+
},
278+
"ProvisioningArtifactId": {
279+
"maxLength": 100,
280+
"minLength": 1,
281+
"type": "string",
282+
},
283+
"ProvisioningArtifactName": {"type": "string"},
284+
"ProvisioningParameters": {
285+
"items": {"$ref": "#/definitions/ProvisioningParameter"},
286+
"type": "array",
287+
},
288+
"ProvisioningPreferences": {
289+
"$ref": "#/definitions/ProvisioningPreferences"
290+
},
291+
"RecordId": {"maxLength": 50, "minLength": 1, "type": "string"},
292+
"Tags": {"items": {"$ref": "#/definitions/Tag"}, "type": "array"},
293+
},
294+
"readOnlyProperties": [
295+
"/properties/RecordId",
296+
"/properties/CloudformationStackArn",
297+
"/properties/Outputs",
298+
"/properties/ProvisionedProductId",
299+
],
300+
"sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-rpdk.git",
301+
"typeName": "AWS::ServiceCatalog::CloudFormationProvisionedProduct",
302+
}
303+
304+
305+
def test_servicecatalog_cloudformation_schema(resource_servicecatalog_schema):
306+
307+
schema = Schema(schema=resource_servicecatalog_schema)
308+
assert list(schema.get_atts.keys()) == [
309+
"RecordId",
310+
"CloudformationStackArn",
311+
"Outputs\\..*",
312+
"ProvisionedProductId",
313+
]
314+
assert schema.get_atts["Outputs.Example"] == "/properties/CfnLintStringType"

0 commit comments

Comments
 (0)