Skip to content

Commit 64164b5

Browse files
authored
increase test coverage (#2715)
1 parent 8392df8 commit 64164b5

File tree

9 files changed

+502
-213
lines changed

9 files changed

+502
-213
lines changed

src/cfnlint/rules/functions/Select.py

Lines changed: 94 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -13,92 +13,107 @@ class Select(CloudFormationLintRule):
1313
description = "Making sure the Select function is properly configured"
1414
source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html"
1515
tags = ["functions", "select"]
16+
supported_functions = [
17+
"Fn::FindInMap",
18+
"Fn::GetAtt",
19+
"Fn::GetAZs",
20+
"Fn::If",
21+
"Fn::Split",
22+
"Fn::Cidr",
23+
"Ref",
24+
]
1625

17-
def match(self, cfn):
26+
def _test_index_obj(self, index_obj, path):
1827
matches = []
28+
if isinstance(index_obj, dict):
29+
if len(index_obj) == 1:
30+
for index_key, _ in index_obj.items():
31+
if index_key not in [
32+
"Ref",
33+
"Fn::FindInMap",
34+
"Fn::Select",
35+
]:
36+
message = "Select index should be an Integer or a function Ref, Fn::FindInMap, or Fn::Select for {0}"
37+
matches.append(
38+
RuleMatch(
39+
path,
40+
message.format("/".join(map(str, path))),
41+
)
42+
)
43+
else:
44+
message = "Select index should be an Integer or a function Ref, Fn::FindInMap, or Fn::Select for {0}"
45+
matches.append(
46+
RuleMatch(
47+
path,
48+
message.format("/".join(map(str, path))),
49+
)
50+
)
51+
elif not isinstance(index_obj, int):
52+
try:
53+
int(index_obj)
54+
except (ValueError, TypeError):
55+
message = "Select index should be an Integer or a function of Ref, Fn::FindInMap, or Fn::Select for {0}"
56+
matches.append(
57+
RuleMatch(path, message.format("/".join(map(str, path))))
58+
)
1959

20-
select_objs = cfn.search_deep_keys("Fn::Select")
21-
22-
supported_functions = [
23-
"Fn::FindInMap",
24-
"Fn::GetAtt",
25-
"Fn::GetAZs",
26-
"Fn::If",
27-
"Fn::Split",
28-
"Fn::Cidr",
29-
"Ref",
30-
]
60+
return matches
3161

32-
for select_obj in select_objs:
33-
select_value_obj = select_obj[-1]
34-
tree = select_obj[:-1]
35-
if isinstance(select_value_obj, list):
36-
if len(select_value_obj) == 2:
37-
index_obj = select_value_obj[0]
38-
list_of_objs = select_value_obj[1]
39-
if isinstance(index_obj, dict):
40-
if len(index_obj) == 1:
41-
for index_key, _ in index_obj.items():
42-
if index_key not in [
43-
"Ref",
44-
"Fn::FindInMap",
45-
"Fn::Select",
46-
]:
47-
message = "Select index should be an Integer or a function Ref or FindInMap for {0}"
48-
matches.append(
49-
RuleMatch(
50-
tree,
51-
message.format("/".join(map(str, tree))),
52-
)
53-
)
54-
elif not isinstance(index_obj, int):
55-
try:
56-
int(index_obj)
57-
except ValueError:
58-
message = "Select index should be an Integer or a function of Ref or FindInMap for {0}"
59-
matches.append(
60-
RuleMatch(
61-
tree, message.format("/".join(map(str, tree)))
62-
)
63-
)
64-
if isinstance(list_of_objs, dict):
65-
if len(list_of_objs) == 1:
66-
for key, _ in list_of_objs.items():
67-
if key not in supported_functions:
68-
message = (
69-
"Select should use a supported function of {0}"
70-
)
71-
matches.append(
72-
RuleMatch(
73-
tree,
74-
message.format(
75-
", ".join(map(str, supported_functions))
76-
),
77-
)
78-
)
79-
else:
80-
message = "Select should use a supported function of {0}"
81-
matches.append(
82-
RuleMatch(
83-
tree,
84-
message.format(
85-
", ".join(map(str, supported_functions))
86-
),
87-
)
88-
)
89-
elif not isinstance(list_of_objs, list):
90-
message = "Select should be an array of values for {0}"
62+
def _test_list_obj(self, list_obj, path):
63+
matches = []
64+
if isinstance(list_obj, dict):
65+
if len(list_obj) == 1:
66+
for key, _ in list_obj.items():
67+
if key not in self.supported_functions:
68+
message = "Select should use a supported function of {0}"
9169
matches.append(
92-
RuleMatch(tree, message.format("/".join(map(str, tree))))
70+
RuleMatch(
71+
path,
72+
message.format(
73+
", ".join(map(str, self.supported_functions))
74+
),
75+
)
9376
)
94-
else:
95-
message = "Select should be a list of 2 elements for {0}"
96-
matches.append(
97-
RuleMatch(tree, message.format("/".join(map(str, tree))))
98-
)
9977
else:
100-
message = "Select should be a list of 2 elements for {0}"
78+
message = "Select should use a supported function of {0}"
10179
matches.append(
102-
RuleMatch(tree, message.format("/".join(map(str, tree))))
80+
RuleMatch(
81+
path,
82+
message.format(", ".join(map(str, self.supported_functions))),
83+
)
10384
)
85+
elif not isinstance(list_obj, list):
86+
message = "Select should be an array of values for {0}"
87+
matches.append(RuleMatch(path, message.format("/".join(map(str, path)))))
88+
89+
return matches
90+
91+
def _test_select_obj(self, select_obj, path):
92+
matches = []
93+
if not isinstance(select_obj, list):
94+
message = "Select should be a list of 2 elements for {0}"
95+
matches.append(RuleMatch(path, message.format("/".join(map(str, path)))))
96+
return matches
97+
if len(select_obj) != 2:
98+
message = "Select should be a list of 2 elements for {0}"
99+
matches.append(RuleMatch(path, message.format("/".join(map(str, path)))))
100+
return matches
101+
102+
index_obj = select_obj[0]
103+
list_of_objs = select_obj[1]
104+
matches.extend(self._test_index_obj(index_obj, path[:] + [0]))
105+
matches.extend(self._test_list_obj(list_of_objs, path[:] + [1]))
106+
107+
return matches
108+
109+
def match(self, cfn):
110+
matches = []
111+
112+
select_objs = cfn.search_deep_keys("Fn::Select")
113+
114+
for select_obj in select_objs:
115+
select_value_obj = select_obj[-1]
116+
tree = select_obj[:-1]
117+
matches.extend(self._test_select_obj(select_value_obj, tree[:]))
118+
104119
return matches

src/cfnlint/rules/functions/Split.py

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -13,77 +13,77 @@ class Split(CloudFormationLintRule):
1313
description = "Making sure the split function is properly configured"
1414
source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-split.html"
1515
tags = ["functions", "split"]
16+
supported_functions = [
17+
"Fn::Base64",
18+
"Fn::FindInMap",
19+
"Fn::GetAZs",
20+
"Fn::GetAtt",
21+
"Fn::If",
22+
"Fn::ImportValue",
23+
"Fn::Join",
24+
"Fn::Select",
25+
"Fn::Sub",
26+
"Ref",
27+
"Fn::ToJsonString",
28+
]
1629

17-
def match(self, cfn):
30+
def _test_delimiter(self, delimiter, path):
1831
matches = []
32+
if not isinstance(delimiter, str):
33+
message = "Split delimiter has to be of type string for {0}"
34+
matches.append(RuleMatch(path, message.format("/".join(map(str, path)))))
35+
return matches
1936

20-
split_objs = cfn.search_deep_keys("Fn::Split")
21-
22-
supported_functions = [
23-
"Fn::Base64",
24-
"Fn::FindInMap",
25-
"Fn::GetAZs",
26-
"Fn::GetAtt",
27-
"Fn::If",
28-
"Fn::ImportValue",
29-
"Fn::Join",
30-
"Fn::Select",
31-
"Fn::Sub",
32-
"Ref",
33-
"Fn::ToJsonString",
34-
]
35-
36-
for split_obj in split_objs:
37-
split_value_obj = split_obj[-1]
38-
tree = split_obj[:-1]
39-
if isinstance(split_value_obj, list):
40-
if len(split_value_obj) == 2:
41-
split_delimiter = split_value_obj[0]
42-
split_string = split_value_obj[1]
43-
if not isinstance(split_delimiter, str):
44-
message = "Split delimiter has to be of type string for {0}"
37+
def _test_string(self, s, path):
38+
matches = []
39+
if isinstance(s, dict):
40+
if len(s) == 1:
41+
for key, _ in s.items():
42+
if key not in self.supported_functions:
43+
message = "Fn::Split doesn't support the function {0} at {1}"
4544
matches.append(
4645
RuleMatch(
47-
tree + [0], message.format("/".join(map(str, tree)))
48-
)
49-
)
50-
if isinstance(split_string, dict):
51-
if len(split_string) == 1:
52-
for key, _ in split_string.items():
53-
if key not in supported_functions:
54-
message = "Fn::Split doesn't support the function {0} at {1}"
55-
matches.append(
56-
RuleMatch(
57-
tree + [key],
58-
message.format(
59-
key, "/".join(map(str, tree))
60-
),
61-
)
62-
)
63-
else:
64-
message = (
65-
"Split list of singular function or string for {0}"
66-
)
67-
matches.append(
68-
RuleMatch(
69-
tree, message.format("/".join(map(str, tree)))
70-
)
46+
path + [key],
47+
message.format(key, "/".join(map(str, path))),
7148
)
72-
elif not isinstance(split_string, str):
73-
message = (
74-
"Split has to be of type string or valid function for {0}"
7549
)
76-
matches.append(
77-
RuleMatch(tree, message.format("/".join(map(str, tree))))
78-
)
79-
else:
80-
message = "Split should be an array of 2 for {0}"
81-
matches.append(
82-
RuleMatch(tree, message.format("/".join(map(str, tree))))
83-
)
8450
else:
85-
message = "Split should be an array of 2 for {0}"
51+
message = "Split list of singular function or string for {0}"
8652
matches.append(
87-
RuleMatch(tree, message.format("/".join(map(str, tree))))
53+
RuleMatch(path, message.format("/".join(map(str, path))))
8854
)
55+
elif not isinstance(s, str):
56+
message = "Split has to be of type string or valid function for {0}"
57+
matches.append(RuleMatch(path, message.format("/".join(map(str, path)))))
58+
return matches
59+
60+
def _test_split(self, split_obj, path):
61+
matches = []
62+
if not isinstance(split_obj, list):
63+
message = "Split should be an array of 2 for {0}"
64+
matches.append(RuleMatch(path, message.format("/".join(map(str, path)))))
65+
return matches
66+
67+
if len(split_obj) != 2:
68+
message = "Split should be an array of 2 for {0}"
69+
matches.append(RuleMatch(path, message.format("/".join(map(str, path)))))
70+
return matches
71+
72+
split_delimiter = split_obj[0]
73+
split_string = split_obj[1]
74+
matches.extend(self._test_delimiter(split_delimiter, path[:] + [0]))
75+
matches.extend(self._test_string(split_string, path[:] + [1]))
76+
77+
return matches
78+
79+
def match(self, cfn):
80+
matches = []
81+
82+
split_objs = cfn.search_deep_keys("Fn::Split")
83+
84+
for split_obj in split_objs:
85+
split_value_obj = split_obj[-1]
86+
tree = split_obj[:-1]
87+
matches.extend(self._test_split(split_value_obj, tree[:]))
88+
8989
return matches

test/fixtures/templates/bad/functions_select.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ Resources:
2828
Fn::Select:
2929
- 1
3030
- !Join [ ',', [ 'a', 'b' ] ]
31+
myInstance3:
32+
Type: AWS::EC2::Instance
33+
Properties:
34+
ImageId: String
35+
AvailabilityZone:
36+
Fn::Select: "foo"

test/fixtures/templates/bad/functions_split.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,10 @@ Resources:
2424
- ','
2525
- !GetAZs ""
2626
- !Ref AWS::Region
27+
myInstance3:
28+
Type: AWS::EC2::Instance
29+
Properties:
30+
ImageId: String
31+
AvailabilityZone: !Select
32+
- 0
33+
- Fn::Split: {Ref: AWS::Region}

0 commit comments

Comments
 (0)