moto/scripts/cloudformation_coverage.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

112 lines
3.6 KiB
Python
Raw Normal View History

#!/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)