112 lines
3.6 KiB
Python
112 lines
3.6 KiB
Python
|
#!/usr/bin/env python
|
||
|
import importlib
|
||
|
import json
|
||
|
import mock
|
||
|
import requests
|
||
|
|
||
|
import moto
|
||
|
|
||
|
# Populate CloudFormationModel.__subclasses__()
|
||
|
moto.mock_all()
|
||
|
|
||
|
|
||
|
def check(condition):
|
||
|
if bool(condition):
|
||
|
return "x"
|
||
|
else:
|
||
|
return " "
|
||
|
|
||
|
|
||
|
def is_implemented(model, method_name):
|
||
|
# method_name in model.__dict__ will be True if the method
|
||
|
# exists on the model and False if it's only inherited from
|
||
|
# CloudFormationModel.
|
||
|
return hasattr(model, method_name) and method_name in model.__dict__
|
||
|
|
||
|
|
||
|
class CloudFormationChecklist:
|
||
|
def __init__(self, resource_name, schema):
|
||
|
self.resource_name = resource_name
|
||
|
self.schema = schema
|
||
|
|
||
|
def __str__(self):
|
||
|
missing_attrs_checklist = "\n".join(
|
||
|
[f" - [ ] {attr}" for attr in self.missing_attrs]
|
||
|
)
|
||
|
report = (
|
||
|
f"- {self.resource_name}:\n"
|
||
|
f" - [{check(self.creatable)}] create implemented\n"
|
||
|
f" - [{check(self.updatable)}] update implemented\n"
|
||
|
f" - [{check(self.deletable)}] delete implemented\n"
|
||
|
f" - [{check(not self.missing_attrs)}] Fn::GetAtt implemented\n"
|
||
|
) + missing_attrs_checklist
|
||
|
|
||
|
return report.strip()
|
||
|
|
||
|
@property
|
||
|
def service_name(self):
|
||
|
return self.resource_name.split("::")[1].lower()
|
||
|
|
||
|
@property
|
||
|
def model_name(self):
|
||
|
return self.resource_name.split("::")[2]
|
||
|
|
||
|
@property
|
||
|
def moto_model(self):
|
||
|
for subclass in moto.core.common_models.CloudFormationModel.__subclasses__():
|
||
|
subclass_service = subclass.__module__.split(".")[1]
|
||
|
subclass_model = subclass.__name__
|
||
|
|
||
|
if subclass_service == self.service_name and subclass_model in (
|
||
|
self.model_name,
|
||
|
"Fake" + self.model_name,
|
||
|
):
|
||
|
return subclass
|
||
|
|
||
|
@property
|
||
|
def expected_attrs(self):
|
||
|
return list(self.schema.get("Attributes", {}).keys())
|
||
|
|
||
|
@property
|
||
|
def missing_attrs(self):
|
||
|
missing_attrs = []
|
||
|
for attr in self.expected_attrs:
|
||
|
try:
|
||
|
# TODO: Change the actual abstract method to return False
|
||
|
with mock.patch(
|
||
|
"moto.core.common_models.CloudFormationModel.has_cfn_attr",
|
||
|
return_value=False,
|
||
|
):
|
||
|
if not self.moto_model.has_cfn_attr(attr):
|
||
|
missing_attrs.append(attr)
|
||
|
except:
|
||
|
missing_attrs.append(attr)
|
||
|
return missing_attrs
|
||
|
|
||
|
@property
|
||
|
def creatable(self):
|
||
|
return is_implemented(self.moto_model, "create_from_cloudformation_json")
|
||
|
|
||
|
@property
|
||
|
def updatable(self):
|
||
|
return is_implemented(self.moto_model, "update_from_cloudformation_json")
|
||
|
|
||
|
@property
|
||
|
def deletable(self):
|
||
|
return is_implemented(self.moto_model, "delete_from_cloudformation_json")
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html
|
||
|
cfn_spec = requests.get(
|
||
|
"https://dnwj8swjjbsbt.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json"
|
||
|
).json()
|
||
|
for resource_name, schema in sorted(cfn_spec["ResourceTypes"].items()):
|
||
|
checklist = CloudFormationChecklist(resource_name, schema)
|
||
|
# Only print checklists for models that implement CloudFormationModel;
|
||
|
# otherwise the checklist is very long and mostly empty because there
|
||
|
# are so many niche AWS services and resources that moto doesn't
|
||
|
# implement yet.
|
||
|
if checklist.moto_model:
|
||
|
print(checklist)
|