-
Notifications
You must be signed in to change notification settings - Fork 598
/
Copy pathInstanceImageId.py
138 lines (122 loc) · 5.01 KB
/
InstanceImageId.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
from __future__ import annotations
from collections import deque
from typing import Any, Iterator
from cfnlint.helpers import ensure_list, is_function
from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
from cfnlint.rules.helpers import get_resource_by_name, get_value_from_path
from cfnlint.rules.jsonschema.CfnLintKeyword import CfnLintKeyword
class InstanceImageId(CfnLintKeyword):
id = "E3673"
shortdesc = "Validate if an ImageId is required"
description = (
"Validate if an ImageID is required. It can be "
"required if the associated LaunchTemplate doesn't specify "
"an ImageID"
)
tags = ["resources", "ec2"]
def __init__(self) -> None:
super().__init__(
keywords=[
"Resources/AWS::EC2::Instance/Properties",
],
)
def _get_related_launch_template(
self, validator: Validator, instance: Any
) -> Iterator[tuple[Any, Validator]]:
for launch_template, launch_template_validator in get_value_from_path(
validator, instance, deque(["LaunchTemplate"])
):
if not launch_template:
continue
for id, id_validator in get_value_from_path(
launch_template_validator, launch_template, deque(["LaunchTemplateId"])
):
if id is None:
for name, name_validator in get_value_from_path(
id_validator,
launch_template,
deque(["LaunchTemplateName"]),
):
yield name, name_validator
yield id, id_validator
def validate(
self, validator: Validator, keywords: Any, instance: Any, schema: dict[str, Any]
) -> ValidationResult:
for (
instance_image_id,
instance_image_id_validator,
) in get_value_from_path(
validator,
instance,
path=deque(["ImageId"]),
):
if instance_image_id:
continue
launch_templates = list(
self._get_related_launch_template(instance_image_id_validator, instance)
)
path: deque[str | int] = deque([])
if "ImageId" != instance_image_id_validator.context.path.path[-1]:
path = deque(
list(instance_image_id_validator.context.path.path)[
len(validator.context.path.path) :
]
)
if not launch_templates:
yield ValidationError(
"'ImageId' is a required property",
validator="required",
path=path,
rule=self,
)
continue
for (
instance_launch_template,
instance_launch_template_validator,
) in launch_templates:
fn_k, fn_v = is_function(instance_launch_template)
# if its not a function we can't tell from a string
# if the image ID is there or not
if fn_k is None:
continue
launch_template, launch_template_validator = None, None
if fn_k == "Ref":
if fn_v in instance_launch_template_validator.context.parameters:
continue
elif fn_v in instance_launch_template_validator.context.resources:
launch_template, launch_template_validator = (
get_resource_by_name(
instance_launch_template_validator,
fn_v,
["AWS::EC2::LaunchTemplate"],
)
)
else:
continue
elif fn_k == "Fn::GetAtt":
launch_template, launch_template_validator = get_resource_by_name(
instance_launch_template_validator,
ensure_list(fn_v)[0].split(".")[0],
["AWS::EC2::LaunchTemplate"],
)
else:
continue
for (
launch_template_image_id,
_,
) in get_value_from_path(
launch_template_validator,
launch_template or {},
path=deque(["Properties", "LaunchTemplateData", "ImageId"]),
):
if launch_template_image_id is None and instance_image_id is None:
yield ValidationError(
"'ImageId' is a required property",
validator="required",
path=path,
rule=self,
)