diff --git a/CLOUDFORMATION_COVERAGE.md b/CLOUDFORMATION_COVERAGE.md new file mode 100644 index 000000000..60619ff07 --- /dev/null +++ b/CLOUDFORMATION_COVERAGE.md @@ -0,0 +1,351 @@ +- AWS::ApiGateway::Deployment: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] DeploymentId +- AWS::ApiGateway::Method: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::ApiGateway::Resource: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] ResourceId +- AWS::AutoScaling::AutoScalingGroup: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] LaunchConfigurationName + - [ ] LaunchTemplateSpecification + - [ ] MixedInstancesPolicy + - [ ] PlacementGroup + - [ ] VPCZoneIdentifier +- AWS::AutoScaling::LaunchConfiguration: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::AutoScaling::ScheduledAction: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] ScheduledActionName +- AWS::Batch::ComputeEnvironment: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] ComputeEnvironmentArn +- AWS::Batch::JobDefinition: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::Batch::JobQueue: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] JobQueueArn +- AWS::CloudFormation::Stack: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::DataPipeline::Pipeline: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::DynamoDB::Table: + - [x] create implemented + - [ ] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::EC2::Instance: + - [x] create implemented + - [ ] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::EC2::InternetGateway: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] InternetGatewayId +- AWS::EC2::LaunchTemplate: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] LatestVersionNumber + - [ ] DefaultVersionNumber +- AWS::EC2::NatGateway: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] NatGatewayId +- AWS::EC2::NetworkInterface: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] Id +- AWS::EC2::Route: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::EC2::RouteTable: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] RouteTableId +- AWS::EC2::SecurityGroup: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] VpcId +- AWS::EC2::SecurityGroupIngress: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::EC2::Subnet: + - [x] create implemented + - [ ] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] VpcId + - [ ] NetworkAclAssociationId + - [ ] OutpostArn + - [ ] SubnetId + - [ ] Ipv6CidrBlocks +- AWS::EC2::SubnetRouteTableAssociation: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] Id +- AWS::EC2::TransitGateway: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] Id +- AWS::EC2::VPC: + - [x] create implemented + - [ ] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] VpcId + - [ ] CidrBlockAssociations + - [ ] CidrBlock + - [ ] DefaultNetworkAcl + - [ ] Ipv6CidrBlocks + - [ ] DefaultSecurityGroup +- AWS::EC2::VPCGatewayAttachment: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::EC2::VPCPeeringConnection: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] Id +- AWS::EC2::Volume: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] VolumeId +- AWS::EC2::VolumeAttachment: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::ECR::Repository: + - [x] create implemented + - [x] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::ECS::Cluster: + - [x] create implemented + - [x] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::ECS::Service: + - [x] create implemented + - [x] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] ServiceArn +- AWS::ECS::TaskDefinition: + - [x] create implemented + - [x] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] TaskDefinitionArn +- AWS::EFS::FileSystem: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] FileSystemId + - [ ] Arn +- AWS::EFS::MountTarget: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] IpAddress + - [ ] Id +- AWS::Events::Archive: + - [x] create implemented + - [x] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::Events::EventBus: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::Events::Rule: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::IAM::AccessKey: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::IAM::InstanceProfile: + - [x] create implemented + - [ ] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::IAM::ManagedPolicy: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::IAM::Policy: + - [ ] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::IAM::Role: + - [x] create implemented + - [ ] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] RoleId +- AWS::IAM::User: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::KMS::Key: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] KeyId +- AWS::Kinesis::Stream: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::Logs::LogGroup: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] Arn +- AWS::RDS::DBParameterGroup: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] DBParameterGroupName +- AWS::Redshift::Cluster: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] Id + - [ ] DeferMaintenanceIdentifier +- AWS::Route53::HealthCheck: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] HealthCheckId +- AWS::Route53::RecordSet: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::Route53::RecordSetGroup: + - [x] create implemented + - [ ] update implemented + - [ ] delete implemented + - [x] Fn::GetAtt implemented +- AWS::S3::Bucket: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::SNS::Topic: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] TopicArn +- AWS::SQS::Queue: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] QueueUrl +- AWS::SSM::Parameter: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] Type + - [ ] Value +- AWS::SageMaker::Endpoint: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::SageMaker::EndpointConfig: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::SageMaker::Model: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [x] Fn::GetAtt implemented +- AWS::StepFunctions::StateMachine: + - [x] create implemented + - [x] update implemented + - [x] delete implemented + - [ ] Fn::GetAtt implemented + - [ ] StateMachineRevisionId + - [ ] Arn diff --git a/Makefile b/Makefile index ac8a9016a..fb85b99b2 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,12 @@ implementation_coverage: ./scripts/implementation_coverage.py git commit IMPLEMENTATION_COVERAGE.md -m "Updating implementation coverage" || true +cloudformation_coverage: + ./scripts/cloudformation_coverage.py > CLOUDFORMATION_COVERAGE.md + git commit CLOUDFORMATION_COVERAGE.md -m "Updating CloudFormation coverage" || true + +coverage: implementation_coverage cloudformation_coverage + scaffold: @pip install -r requirements-dev.txt > /dev/null exec python scripts/scaffold.py diff --git a/scripts/cloudformation_coverage.py b/scripts/cloudformation_coverage.py new file mode 100755 index 000000000..cb00dace5 --- /dev/null +++ b/scripts/cloudformation_coverage.py @@ -0,0 +1,111 @@ +#!/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)