Improve implementation coverage (and layout)

This commit is contained in:
Bert Blommers 2020-05-12 14:58:35 +01:00
parent 34911b7c8b
commit ddb5c30d34
14 changed files with 1243 additions and 387 deletions

File diff suppressed because it is too large Load Diff

View File

@ -49,9 +49,7 @@ mock_dynamodbstreams = lazy_load(".dynamodbstreams", "mock_dynamodbstreams")
mock_elasticbeanstalk = lazy_load(".elasticbeanstalk", "mock_elasticbeanstalk") mock_elasticbeanstalk = lazy_load(".elasticbeanstalk", "mock_elasticbeanstalk")
mock_ec2 = lazy_load(".ec2", "mock_ec2") mock_ec2 = lazy_load(".ec2", "mock_ec2")
mock_ec2_deprecated = lazy_load(".ec2", "mock_ec2_deprecated") mock_ec2_deprecated = lazy_load(".ec2", "mock_ec2_deprecated")
mock_ec2_instance_connect = lazy_load( mock_ec2instanceconnect = lazy_load(".ec2instanceconnect", "mock_ec2instanceconnect")
".ec2_instance_connect", "mock_ec2_instance_connect"
)
mock_ecr = lazy_load(".ecr", "mock_ecr") mock_ecr = lazy_load(".ecr", "mock_ecr")
mock_ecr_deprecated = lazy_load(".ecr", "mock_ecr_deprecated") mock_ecr_deprecated = lazy_load(".ecr", "mock_ecr_deprecated")
mock_ecs = lazy_load(".ecs", "mock_ecs") mock_ecs = lazy_load(".ecs", "mock_ecs")

View File

@ -21,7 +21,7 @@ BACKENDS = {
"dynamodb2": ("dynamodb2", "dynamodb_backends2"), "dynamodb2": ("dynamodb2", "dynamodb_backends2"),
"dynamodbstreams": ("dynamodbstreams", "dynamodbstreams_backends"), "dynamodbstreams": ("dynamodbstreams", "dynamodbstreams_backends"),
"ec2": ("ec2", "ec2_backends"), "ec2": ("ec2", "ec2_backends"),
"ec2_instance_connect": ("ec2_instance_connect", "ec2_instance_connect_backends"), "ec2instanceconnect": ("ec2instanceconnect", "ec2instanceconnect_backends"),
"ecr": ("ecr", "ecr_backends"), "ecr": ("ecr", "ecr_backends"),
"ecs": ("ecs", "ecs_backends"), "ecs": ("ecs", "ecs_backends"),
"elasticbeanstalk": ("elasticbeanstalk", "eb_backends"), "elasticbeanstalk": ("elasticbeanstalk", "eb_backends"),

View File

@ -824,6 +824,42 @@ class DynamoDBBackend(BaseBackend):
required_table = self.tables[table] required_table = self.tables[table]
return required_table.tags return required_table.tags
def list_tables(self, limit, exclusive_start_table_name):
all_tables = list(self.tables.keys())
if exclusive_start_table_name:
try:
last_table_index = all_tables.index(exclusive_start_table_name)
except ValueError:
start = len(all_tables)
else:
start = last_table_index + 1
else:
start = 0
if limit:
tables = all_tables[start : start + limit]
else:
tables = all_tables[start:]
if limit and len(all_tables) > start + limit:
return tables, tables[-1]
return tables, None
def describe_table(self, name):
table = self.tables[name]
return table.describe(base_key="Table")
def update_table(self, name, global_index, throughput, stream_spec):
table = self.get_table(name)
if global_index:
table = self.update_table_global_indexes(name, global_index)
if throughput:
table = self.update_table_throughput(name, throughput)
if stream_spec:
table = self.update_table_streams(name, stream_spec)
return table
def update_table_throughput(self, name, throughput): def update_table_throughput(self, name, throughput):
table = self.tables[name] table = self.tables[name]
table.throughput = throughput table.throughput = throughput
@ -1134,7 +1170,7 @@ class DynamoDBBackend(BaseBackend):
return table.delete_item(hash_value, range_value) return table.delete_item(hash_value, range_value)
def update_ttl(self, table_name, ttl_spec): def update_time_to_live(self, table_name, ttl_spec):
table = self.tables.get(table_name) table = self.tables.get(table_name)
if table is None: if table is None:
raise JsonRESTError("ResourceNotFound", "Table not found") raise JsonRESTError("ResourceNotFound", "Table not found")
@ -1151,7 +1187,7 @@ class DynamoDBBackend(BaseBackend):
table.ttl["TimeToLiveStatus"] = "DISABLED" table.ttl["TimeToLiveStatus"] = "DISABLED"
table.ttl["AttributeName"] = ttl_spec["AttributeName"] table.ttl["AttributeName"] = ttl_spec["AttributeName"]
def describe_ttl(self, table_name): def describe_time_to_live(self, table_name):
table = self.tables.get(table_name) table = self.tables.get(table_name)
if table is None: if table is None:
raise JsonRESTError("ResourceNotFound", "Table not found") raise JsonRESTError("ResourceNotFound", "Table not found")
@ -1246,6 +1282,21 @@ class DynamoDBBackend(BaseBackend):
self.tables = original_table_state self.tables = original_table_state
raise raise
######################
# LIST of methods where the logic completely resides in responses.py
# Duplicated here so that the implementation coverage script is aware
# TODO: Move logic here
######################
def batch_get_item(self):
pass
def batch_write_item(self):
pass
def transact_get_items(self):
pass
dynamodb_backends = {} dynamodb_backends = {}
for region in Session().get_available_regions("dynamodb"): for region in Session().get_available_regions("dynamodb"):

View File

@ -92,27 +92,14 @@ class DynamoHandler(BaseResponse):
def list_tables(self): def list_tables(self):
body = self.body body = self.body
limit = body.get("Limit", 100) limit = body.get("Limit", 100)
all_tables = list(self.dynamodb_backend.tables.keys())
exclusive_start_table_name = body.get("ExclusiveStartTableName") exclusive_start_table_name = body.get("ExclusiveStartTableName")
if exclusive_start_table_name: tables, last_eval = self.dynamodb_backend.list_tables(
try: limit, exclusive_start_table_name
last_table_index = all_tables.index(exclusive_start_table_name) )
except ValueError:
start = len(all_tables)
else:
start = last_table_index + 1
else:
start = 0
if limit:
tables = all_tables[start : start + limit]
else:
tables = all_tables[start:]
response = {"TableNames": tables} response = {"TableNames": tables}
if limit and len(all_tables) > start + limit: if last_eval:
response["LastEvaluatedTableName"] = tables[-1] response["LastEvaluatedTableName"] = last_eval
return dynamo_json_dump(response) return dynamo_json_dump(response)
@ -232,33 +219,29 @@ class DynamoHandler(BaseResponse):
def update_table(self): def update_table(self):
name = self.body["TableName"] name = self.body["TableName"]
table = self.dynamodb_backend.get_table(name) global_index = self.body.get("GlobalSecondaryIndexUpdates", None)
if "GlobalSecondaryIndexUpdates" in self.body: throughput = self.body.get("ProvisionedThroughput", None)
table = self.dynamodb_backend.update_table_global_indexes( stream_spec = self.body.get("StreamSpecification", None)
name, self.body["GlobalSecondaryIndexUpdates"] try:
table = self.dynamodb_backend.update_table(
name=name,
global_index=global_index,
throughput=throughput,
stream_spec=stream_spec,
) )
if "ProvisionedThroughput" in self.body: return dynamo_json_dump(table.describe())
throughput = self.body["ProvisionedThroughput"] except ValueError:
table = self.dynamodb_backend.update_table_throughput(name, throughput) er = "com.amazonaws.dynamodb.v20111205#ResourceInUseException"
if "StreamSpecification" in self.body: return self.error(er, "Cannot enable stream")
try:
table = self.dynamodb_backend.update_table_streams(
name, self.body["StreamSpecification"]
)
except ValueError:
er = "com.amazonaws.dynamodb.v20111205#ResourceInUseException"
return self.error(er, "Cannot enable stream")
return dynamo_json_dump(table.describe())
def describe_table(self): def describe_table(self):
name = self.body["TableName"] name = self.body["TableName"]
try: try:
table = self.dynamodb_backend.tables[name] table = self.dynamodb_backend.describe_table(name)
return dynamo_json_dump(table)
except KeyError: except KeyError:
er = "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException" er = "com.amazonaws.dynamodb.v20111205#ResourceNotFoundException"
return self.error(er, "Requested resource not found") return self.error(er, "Requested resource not found")
return dynamo_json_dump(table.describe(base_key="Table"))
def put_item(self): def put_item(self):
name = self.body["TableName"] name = self.body["TableName"]
@ -850,14 +833,14 @@ class DynamoHandler(BaseResponse):
name = self.body["TableName"] name = self.body["TableName"]
ttl_spec = self.body["TimeToLiveSpecification"] ttl_spec = self.body["TimeToLiveSpecification"]
self.dynamodb_backend.update_ttl(name, ttl_spec) self.dynamodb_backend.update_time_to_live(name, ttl_spec)
return json.dumps({"TimeToLiveSpecification": ttl_spec}) return json.dumps({"TimeToLiveSpecification": ttl_spec})
def describe_time_to_live(self): def describe_time_to_live(self):
name = self.body["TableName"] name = self.body["TableName"]
ttl_spec = self.dynamodb_backend.describe_ttl(name) ttl_spec = self.dynamodb_backend.describe_time_to_live(name)
return json.dumps({"TimeToLiveDescription": ttl_spec}) return json.dumps({"TimeToLiveDescription": ttl_spec})

View File

@ -1,4 +0,0 @@
from ..core.models import base_decorator
from .models import ec2_instance_connect_backends
mock_ec2_instance_connect = base_decorator(ec2_instance_connect_backends)

View File

@ -1,11 +0,0 @@
import boto.ec2
from moto.core import BaseBackend
class Ec2InstanceConnectBackend(BaseBackend):
pass
ec2_instance_connect_backends = {}
for region in boto.ec2.regions():
ec2_instance_connect_backends[region.name] = Ec2InstanceConnectBackend()

View File

@ -1,9 +0,0 @@
import json
from moto.core.responses import BaseResponse
class Ec2InstanceConnectResponse(BaseResponse):
def send_ssh_public_key(self):
return json.dumps(
{"RequestId": "example-2a47-4c91-9700-e37e85162cb6", "Success": True}
)

View File

@ -0,0 +1,4 @@
from ..core.models import base_decorator
from .models import ec2instanceconnect_backends
mock_ec2instanceconnect = base_decorator(ec2instanceconnect_backends)

View File

@ -0,0 +1,15 @@
import boto.ec2
import json
from moto.core import BaseBackend
class Ec2InstanceConnectBackend(BaseBackend):
def send_ssh_public_key(self):
return json.dumps(
{"RequestId": "example-2a47-4c91-9700-e37e85162cb6", "Success": True}
)
ec2instanceconnect_backends = {}
for region in boto.ec2.regions():
ec2instanceconnect_backends[region.name] = Ec2InstanceConnectBackend()

View File

@ -0,0 +1,11 @@
from moto.core.responses import BaseResponse
from .models import ec2instanceconnect_backends
class Ec2InstanceConnectResponse(BaseResponse):
@property
def ec2instanceconnect_backend(self):
return ec2instanceconnect_backends[self.region]
def send_ssh_public_key(self):
return self.ec2instanceconnect_backend.send_ssh_public_key()

View File

@ -7,18 +7,18 @@ import boto3
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
alternative_service_names = {'lambda': 'awslambda'} alternative_service_names = {'lambda': 'awslambda', 'dynamodb': 'dynamodb2'}
def get_moto_implementation(service_name): def get_moto_implementation(service_name):
service_name = service_name.replace("-", "") if "-" in service_name else service_name service_name = service_name.replace("-", "") if "-" in service_name else service_name
alt_service_name = alternative_service_names[service_name] if service_name in alternative_service_names else service_name alt_service_name = alternative_service_names[service_name] if service_name in alternative_service_names else service_name
if not hasattr(moto, alt_service_name): if hasattr(moto, "mock_{}".format(alt_service_name)):
return None mock = getattr(moto, "mock_{}".format(alt_service_name))
module = getattr(moto, alt_service_name) elif hasattr(moto, "mock_{}".format(service_name)):
if module is None: mock = getattr(moto, "mock_{}".format(service_name))
return None else:
mock = getattr(module, "mock_{}".format(service_name)) mock = None
if mock is None: if mock is None:
return None return None
backends = list(mock().backends.values()) backends = list(mock().backends.values())
@ -97,12 +97,14 @@ def write_implementation_coverage_to_file(coverage):
file.write("\n") file.write("\n")
file.write("## {}\n".format(service_name)) file.write("## {}\n".format(service_name))
file.write("{}% implemented\n".format(percentage_implemented)) file.write("<details>\n")
file.write("<summary>{}% implemented</summary>\n\n".format(percentage_implemented))
for op in operations: for op in operations:
if op in implemented: if op in implemented:
file.write("- [X] {}\n".format(op)) file.write("- [X] {}\n".format(op))
else: else:
file.write("- [ ] {}\n".format(op)) file.write("- [ ] {}\n".format(op))
file.write("</details>\n")
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,6 +1,6 @@
import boto3 import boto3
from moto import mock_ec2_instance_connect from moto import mock_ec2instanceconnect
pubkey = """ssh-rsa pubkey = """ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQDV5+voluw2zmzqpqCAqtsyoP01TQ8Ydx1eS1yD6wUsHcPqMIqpo57YxiC8XPwrdeKQ6GG6MC3bHsgXoPypGP0LyixbiuLTU31DnnqorcHt4bWs6rQa7dK2pCCflz2fhYRt5ZjqSNsAKivIbqkH66JozN0SySIka3kEV79GdB0BicioKeEJlCwM9vvxafyzjWf/z8E0lh4ni3vkLpIVJ0t5l+Qd9QMJrT6Is0SCQPVagTYZoi8+fWDoGsBa8vyRwDjEzBl28ZplKh9tSyDkRIYszWTpmK8qHiqjLYZBfAxXjGJbEYL1iig4ZxvbYzKEiKSBi1ZMW9iWjHfZDZuxXAmB AAAAB3NzaC1yc2EAAAADAQABAAABAQDV5+voluw2zmzqpqCAqtsyoP01TQ8Ydx1eS1yD6wUsHcPqMIqpo57YxiC8XPwrdeKQ6GG6MC3bHsgXoPypGP0LyixbiuLTU31DnnqorcHt4bWs6rQa7dK2pCCflz2fhYRt5ZjqSNsAKivIbqkH66JozN0SySIka3kEV79GdB0BicioKeEJlCwM9vvxafyzjWf/z8E0lh4ni3vkLpIVJ0t5l+Qd9QMJrT6Is0SCQPVagTYZoi8+fWDoGsBa8vyRwDjEzBl28ZplKh9tSyDkRIYszWTpmK8qHiqjLYZBfAxXjGJbEYL1iig4ZxvbYzKEiKSBi1ZMW9iWjHfZDZuxXAmB
@ -8,7 +8,7 @@ example
""" """
@mock_ec2_instance_connect @mock_ec2instanceconnect
def test_send_ssh_public_key(): def test_send_ssh_public_key():
client = boto3.client("ec2-instance-connect", region_name="us-east-1") client = boto3.client("ec2-instance-connect", region_name="us-east-1")
fake_request_id = "example-2a47-4c91-9700-e37e85162cb6" fake_request_id = "example-2a47-4c91-9700-e37e85162cb6"