commit
4c2667648a
@ -85,6 +85,15 @@ class NoMethodDefined(BadRequestException):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizerNotFoundException(RESTError):
|
||||||
|
code = 404
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(AuthorizerNotFoundException, self).__init__(
|
||||||
|
"NotFoundException", "Invalid Authorizer identifier specified"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class StageNotFoundException(RESTError):
|
class StageNotFoundException(RESTError):
|
||||||
code = 404
|
code = 404
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ from .exceptions import (
|
|||||||
InvalidHttpEndpoint,
|
InvalidHttpEndpoint,
|
||||||
InvalidResourcePathException,
|
InvalidResourcePathException,
|
||||||
InvalidRequestInput,
|
InvalidRequestInput,
|
||||||
|
AuthorizerNotFoundException,
|
||||||
StageNotFoundException,
|
StageNotFoundException,
|
||||||
RoleNotSpecified,
|
RoleNotSpecified,
|
||||||
NoIntegrationDefined,
|
NoIntegrationDefined,
|
||||||
@ -187,6 +188,54 @@ class Resource(BaseModel):
|
|||||||
return self.resource_methods[method_type].pop("methodIntegration")
|
return self.resource_methods[method_type].pop("methodIntegration")
|
||||||
|
|
||||||
|
|
||||||
|
class Authorizer(BaseModel, dict):
|
||||||
|
def __init__(self, id, name, authorizer_type, **kwargs):
|
||||||
|
super(Authorizer, self).__init__()
|
||||||
|
self["id"] = id
|
||||||
|
self["name"] = name
|
||||||
|
self["type"] = authorizer_type
|
||||||
|
if kwargs.get("provider_arns"):
|
||||||
|
self["providerARNs"] = kwargs.get("provider_arns")
|
||||||
|
if kwargs.get("auth_type"):
|
||||||
|
self["authType"] = kwargs.get("auth_type")
|
||||||
|
if kwargs.get("authorizer_uri"):
|
||||||
|
self["authorizerUri"] = kwargs.get("authorizer_uri")
|
||||||
|
if kwargs.get("authorizer_credentials"):
|
||||||
|
self["authorizerCredentials"] = kwargs.get("authorizer_credentials")
|
||||||
|
if kwargs.get("identity_source"):
|
||||||
|
self["identitySource"] = kwargs.get("identity_source")
|
||||||
|
if kwargs.get("identity_validation_expression"):
|
||||||
|
self["identityValidationExpression"] = kwargs.get(
|
||||||
|
"identity_validation_expression"
|
||||||
|
)
|
||||||
|
self["authorizerResultTtlInSeconds"] = kwargs.get("authorizer_result_ttl")
|
||||||
|
|
||||||
|
def apply_operations(self, patch_operations):
|
||||||
|
for op in patch_operations:
|
||||||
|
if "/authorizerUri" in op["path"]:
|
||||||
|
self["authorizerUri"] = op["value"]
|
||||||
|
elif "/authorizerCredentials" in op["path"]:
|
||||||
|
self["authorizerCredentials"] = op["value"]
|
||||||
|
elif "/authorizerResultTtlInSeconds" in op["path"]:
|
||||||
|
self["authorizerResultTtlInSeconds"] = int(op["value"])
|
||||||
|
elif "/authType" in op["path"]:
|
||||||
|
self["authType"] = op["value"]
|
||||||
|
elif "/identitySource" in op["path"]:
|
||||||
|
self["identitySource"] = op["value"]
|
||||||
|
elif "/identityValidationExpression" in op["path"]:
|
||||||
|
self["identityValidationExpression"] = op["value"]
|
||||||
|
elif "/name" in op["path"]:
|
||||||
|
self["name"] = op["value"]
|
||||||
|
elif "/providerARNs" in op["path"]:
|
||||||
|
# TODO: add and remove
|
||||||
|
raise Exception('Patch operation for "%s" not implemented' % op["path"])
|
||||||
|
elif "/type" in op["path"]:
|
||||||
|
self["type"] = op["value"]
|
||||||
|
else:
|
||||||
|
raise Exception('Patch operation "%s" not implemented' % op["op"])
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class Stage(BaseModel, dict):
|
class Stage(BaseModel, dict):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -412,6 +461,7 @@ class RestAPI(BaseModel):
|
|||||||
self.tags = kwargs.get("tags") or {}
|
self.tags = kwargs.get("tags") or {}
|
||||||
|
|
||||||
self.deployments = {}
|
self.deployments = {}
|
||||||
|
self.authorizers = {}
|
||||||
self.stages = {}
|
self.stages = {}
|
||||||
|
|
||||||
self.resources = {}
|
self.resources = {}
|
||||||
@ -479,6 +529,34 @@ class RestAPI(BaseModel):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def create_authorizer(
|
||||||
|
self,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
authorizer_type,
|
||||||
|
provider_arns=None,
|
||||||
|
auth_type=None,
|
||||||
|
authorizer_uri=None,
|
||||||
|
authorizer_credentials=None,
|
||||||
|
identity_source=None,
|
||||||
|
identiy_validation_expression=None,
|
||||||
|
authorizer_result_ttl=None,
|
||||||
|
):
|
||||||
|
authorizer = Authorizer(
|
||||||
|
id=id,
|
||||||
|
name=name,
|
||||||
|
authorizer_type=authorizer_type,
|
||||||
|
provider_arns=provider_arns,
|
||||||
|
auth_type=auth_type,
|
||||||
|
authorizer_uri=authorizer_uri,
|
||||||
|
authorizer_credentials=authorizer_credentials,
|
||||||
|
identity_source=identity_source,
|
||||||
|
identiy_validation_expression=identiy_validation_expression,
|
||||||
|
authorizer_result_ttl=authorizer_result_ttl,
|
||||||
|
)
|
||||||
|
self.authorizers[id] = authorizer
|
||||||
|
return authorizer
|
||||||
|
|
||||||
def create_stage(
|
def create_stage(
|
||||||
self,
|
self,
|
||||||
name,
|
name,
|
||||||
@ -518,6 +596,9 @@ class RestAPI(BaseModel):
|
|||||||
def get_deployment(self, deployment_id):
|
def get_deployment(self, deployment_id):
|
||||||
return self.deployments[deployment_id]
|
return self.deployments[deployment_id]
|
||||||
|
|
||||||
|
def get_authorizers(self):
|
||||||
|
return list(self.authorizers.values())
|
||||||
|
|
||||||
def get_stages(self):
|
def get_stages(self):
|
||||||
return list(self.stages.values())
|
return list(self.stages.values())
|
||||||
|
|
||||||
@ -613,6 +694,46 @@ class APIGatewayBackend(BaseBackend):
|
|||||||
)
|
)
|
||||||
return method
|
return method
|
||||||
|
|
||||||
|
def get_authorizer(self, restapi_id, authorizer_id):
|
||||||
|
api = self.get_rest_api(restapi_id)
|
||||||
|
authorizer = api.authorizers.get(authorizer_id)
|
||||||
|
if authorizer is None:
|
||||||
|
raise AuthorizerNotFoundException()
|
||||||
|
else:
|
||||||
|
return authorizer
|
||||||
|
|
||||||
|
def get_authorizers(self, restapi_id):
|
||||||
|
api = self.get_rest_api(restapi_id)
|
||||||
|
return api.get_authorizers()
|
||||||
|
|
||||||
|
def create_authorizer(self, restapi_id, name, authorizer_type, **kwargs):
|
||||||
|
api = self.get_rest_api(restapi_id)
|
||||||
|
authorizer_id = create_id()
|
||||||
|
authorizer = api.create_authorizer(
|
||||||
|
authorizer_id,
|
||||||
|
name,
|
||||||
|
authorizer_type,
|
||||||
|
provider_arns=kwargs.get("provider_arns"),
|
||||||
|
auth_type=kwargs.get("auth_type"),
|
||||||
|
authorizer_uri=kwargs.get("authorizer_uri"),
|
||||||
|
authorizer_credentials=kwargs.get("authorizer_credentials"),
|
||||||
|
identity_source=kwargs.get("identity_source"),
|
||||||
|
identiy_validation_expression=kwargs.get("identiy_validation_expression"),
|
||||||
|
authorizer_result_ttl=kwargs.get("authorizer_result_ttl"),
|
||||||
|
)
|
||||||
|
return api.authorizers.get(authorizer["id"])
|
||||||
|
|
||||||
|
def update_authorizer(self, restapi_id, authorizer_id, patch_operations):
|
||||||
|
authorizer = self.get_authorizer(restapi_id, authorizer_id)
|
||||||
|
if not authorizer:
|
||||||
|
api = self.get_rest_api(restapi_id)
|
||||||
|
authorizer = api.authorizers[authorizer_id] = Authorizer()
|
||||||
|
return authorizer.apply_operations(patch_operations)
|
||||||
|
|
||||||
|
def delete_authorizer(self, restapi_id, authorizer_id):
|
||||||
|
api = self.get_rest_api(restapi_id)
|
||||||
|
del api.authorizers[authorizer_id]
|
||||||
|
|
||||||
def get_stage(self, function_id, stage_name):
|
def get_stage(self, function_id, stage_name):
|
||||||
api = self.get_rest_api(function_id)
|
api = self.get_rest_api(function_id)
|
||||||
stage = api.stages.get(stage_name)
|
stage = api.stages.get(stage_name)
|
||||||
|
@ -8,11 +8,13 @@ from .exceptions import (
|
|||||||
ApiKeyNotFoundException,
|
ApiKeyNotFoundException,
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
CrossAccountNotAllowed,
|
CrossAccountNotAllowed,
|
||||||
|
AuthorizerNotFoundException,
|
||||||
StageNotFoundException,
|
StageNotFoundException,
|
||||||
ApiKeyAlreadyExists,
|
ApiKeyAlreadyExists,
|
||||||
)
|
)
|
||||||
|
|
||||||
API_KEY_SOURCES = ["AUTHORIZER", "HEADER"]
|
API_KEY_SOURCES = ["AUTHORIZER", "HEADER"]
|
||||||
|
AUTHORIZER_TYPES = ["TOKEN", "REQUEST", "COGNITO_USER_POOLS"]
|
||||||
ENDPOINT_CONFIGURATION_TYPES = ["PRIVATE", "EDGE", "REGIONAL"]
|
ENDPOINT_CONFIGURATION_TYPES = ["PRIVATE", "EDGE", "REGIONAL"]
|
||||||
|
|
||||||
|
|
||||||
@ -177,6 +179,88 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
return 200, {}, json.dumps(method_response)
|
return 200, {}, json.dumps(method_response)
|
||||||
|
|
||||||
|
def restapis_authorizers(self, request, full_url, headers):
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
url_path_parts = self.path.split("/")
|
||||||
|
restapi_id = url_path_parts[2]
|
||||||
|
|
||||||
|
if self.method == "POST":
|
||||||
|
name = self._get_param("name")
|
||||||
|
authorizer_type = self._get_param("type")
|
||||||
|
|
||||||
|
provider_arns = self._get_param_with_default_value("providerARNs", None)
|
||||||
|
auth_type = self._get_param_with_default_value("authType", None)
|
||||||
|
authorizer_uri = self._get_param_with_default_value("authorizerUri", None)
|
||||||
|
authorizer_credentials = self._get_param_with_default_value(
|
||||||
|
"authorizerCredentials", None
|
||||||
|
)
|
||||||
|
identity_source = self._get_param_with_default_value("identitySource", None)
|
||||||
|
identiy_validation_expression = self._get_param_with_default_value(
|
||||||
|
"identityValidationExpression", None
|
||||||
|
)
|
||||||
|
authorizer_result_ttl = self._get_param_with_default_value(
|
||||||
|
"authorizerResultTtlInSeconds", 300
|
||||||
|
)
|
||||||
|
|
||||||
|
# Param validation
|
||||||
|
if authorizer_type and authorizer_type not in AUTHORIZER_TYPES:
|
||||||
|
return self.error(
|
||||||
|
"ValidationException",
|
||||||
|
(
|
||||||
|
"1 validation error detected: "
|
||||||
|
"Value '{authorizer_type}' at 'createAuthorizerInput.type' failed "
|
||||||
|
"to satisfy constraint: Member must satisfy enum value set: "
|
||||||
|
"[TOKEN, REQUEST, COGNITO_USER_POOLS]"
|
||||||
|
).format(authorizer_type=authorizer_type),
|
||||||
|
)
|
||||||
|
|
||||||
|
authorizer_response = self.backend.create_authorizer(
|
||||||
|
restapi_id,
|
||||||
|
name,
|
||||||
|
authorizer_type,
|
||||||
|
provider_arns=provider_arns,
|
||||||
|
auth_type=auth_type,
|
||||||
|
authorizer_uri=authorizer_uri,
|
||||||
|
authorizer_credentials=authorizer_credentials,
|
||||||
|
identity_source=identity_source,
|
||||||
|
identiy_validation_expression=identiy_validation_expression,
|
||||||
|
authorizer_result_ttl=authorizer_result_ttl,
|
||||||
|
)
|
||||||
|
elif self.method == "GET":
|
||||||
|
authorizers = self.backend.get_authorizers(restapi_id)
|
||||||
|
return 200, {}, json.dumps({"item": authorizers})
|
||||||
|
|
||||||
|
return 200, {}, json.dumps(authorizer_response)
|
||||||
|
|
||||||
|
def authorizers(self, request, full_url, headers):
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
url_path_parts = self.path.split("/")
|
||||||
|
restapi_id = url_path_parts[2]
|
||||||
|
authorizer_id = url_path_parts[4]
|
||||||
|
|
||||||
|
if self.method == "GET":
|
||||||
|
try:
|
||||||
|
authorizer_response = self.backend.get_authorizer(
|
||||||
|
restapi_id, authorizer_id
|
||||||
|
)
|
||||||
|
except AuthorizerNotFoundException as error:
|
||||||
|
return (
|
||||||
|
error.code,
|
||||||
|
{},
|
||||||
|
'{{"message":"{0}","code":"{1}"}}'.format(
|
||||||
|
error.message, error.error_type
|
||||||
|
),
|
||||||
|
)
|
||||||
|
elif self.method == "PATCH":
|
||||||
|
patch_operations = self._get_param("patchOperations")
|
||||||
|
authorizer_response = self.backend.update_authorizer(
|
||||||
|
restapi_id, authorizer_id, patch_operations
|
||||||
|
)
|
||||||
|
elif self.method == "DELETE":
|
||||||
|
self.backend.delete_authorizer(restapi_id, authorizer_id)
|
||||||
|
return 202, {}, "{}"
|
||||||
|
return 200, {}, json.dumps(authorizer_response)
|
||||||
|
|
||||||
def restapis_stages(self, request, full_url, headers):
|
def restapis_stages(self, request, full_url, headers):
|
||||||
self.setup_class(request, full_url, headers)
|
self.setup_class(request, full_url, headers)
|
||||||
url_path_parts = self.path.split("/")
|
url_path_parts = self.path.split("/")
|
||||||
|
@ -7,6 +7,8 @@ url_paths = {
|
|||||||
"{0}/restapis$": APIGatewayResponse().restapis,
|
"{0}/restapis$": APIGatewayResponse().restapis,
|
||||||
"{0}/restapis/(?P<function_id>[^/]+)/?$": APIGatewayResponse().restapis_individual,
|
"{0}/restapis/(?P<function_id>[^/]+)/?$": APIGatewayResponse().restapis_individual,
|
||||||
"{0}/restapis/(?P<function_id>[^/]+)/resources$": APIGatewayResponse().resources,
|
"{0}/restapis/(?P<function_id>[^/]+)/resources$": APIGatewayResponse().resources,
|
||||||
|
"{0}/restapis/(?P<function_id>[^/]+)/authorizers$": APIGatewayResponse().restapis_authorizers,
|
||||||
|
"{0}/restapis/(?P<function_id>[^/]+)/authorizers/(?P<authorizer_id>[^/]+)/?$": APIGatewayResponse().authorizers,
|
||||||
"{0}/restapis/(?P<function_id>[^/]+)/stages$": APIGatewayResponse().restapis_stages,
|
"{0}/restapis/(?P<function_id>[^/]+)/stages$": APIGatewayResponse().restapis_stages,
|
||||||
"{0}/restapis/(?P<function_id>[^/]+)/stages/(?P<stage_name>[^/]+)/?$": APIGatewayResponse().stages,
|
"{0}/restapis/(?P<function_id>[^/]+)/stages/(?P<stage_name>[^/]+)/?$": APIGatewayResponse().stages,
|
||||||
"{0}/restapis/(?P<function_id>[^/]+)/deployments$": APIGatewayResponse().deployments,
|
"{0}/restapis/(?P<function_id>[^/]+)/deployments$": APIGatewayResponse().deployments,
|
||||||
|
@ -1406,6 +1406,7 @@ class DynamoDBBackend(BaseBackend):
|
|||||||
range_value = None
|
range_value = None
|
||||||
|
|
||||||
item = table.get_item(hash_value, range_value)
|
item = table.get_item(hash_value, range_value)
|
||||||
|
orig_item = copy.deepcopy(item)
|
||||||
|
|
||||||
if not expected:
|
if not expected:
|
||||||
expected = {}
|
expected = {}
|
||||||
@ -1439,6 +1440,8 @@ class DynamoDBBackend(BaseBackend):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
item.update_with_attribute_updates(attribute_updates)
|
item.update_with_attribute_updates(attribute_updates)
|
||||||
|
if table.stream_shard is not None:
|
||||||
|
table.stream_shard.add(orig_item, item)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def delete_item(
|
def delete_item(
|
||||||
|
@ -86,6 +86,9 @@ class FakeStep(BaseModel):
|
|||||||
self.start_datetime = None
|
self.start_datetime = None
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.start_datetime = datetime.now(pytz.utc)
|
||||||
|
|
||||||
|
|
||||||
class FakeCluster(BaseModel):
|
class FakeCluster(BaseModel):
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -204,6 +207,8 @@ class FakeCluster(BaseModel):
|
|||||||
|
|
||||||
self.start_cluster()
|
self.start_cluster()
|
||||||
self.run_bootstrap_actions()
|
self.run_bootstrap_actions()
|
||||||
|
if self.steps:
|
||||||
|
self.steps[0].start()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def instance_groups(self):
|
def instance_groups(self):
|
||||||
|
@ -835,7 +835,7 @@ LIST_STEPS_TEMPLATE = """<ListStepsResponse xmlns="http://elasticmapreduce.amazo
|
|||||||
{% if step.end_datetime is not none %}
|
{% if step.end_datetime is not none %}
|
||||||
<EndDateTime>{{ step.end_datetime.isoformat() }}</EndDateTime>
|
<EndDateTime>{{ step.end_datetime.isoformat() }}</EndDateTime>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if step.ready_datetime is not none %}
|
{% if step.start_datetime is not none %}
|
||||||
<StartDateTime>{{ step.start_datetime.isoformat() }}</StartDateTime>
|
<StartDateTime>{{ step.start_datetime.isoformat() }}</StartDateTime>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</Timeline>
|
</Timeline>
|
||||||
|
@ -8,7 +8,8 @@ from boto3 import Session
|
|||||||
|
|
||||||
from moto.core import BaseBackend, BaseModel
|
from moto.core import BaseBackend, BaseModel
|
||||||
from moto.core.utils import unix_time
|
from moto.core.utils import unix_time
|
||||||
|
from moto.utilities.tagging_service import TaggingService
|
||||||
|
from moto.core.exceptions import JsonRESTError
|
||||||
from moto.iam.models import ACCOUNT_ID
|
from moto.iam.models import ACCOUNT_ID
|
||||||
|
|
||||||
from .utils import decrypt, encrypt, generate_key_id, generate_master_key
|
from .utils import decrypt, encrypt, generate_key_id, generate_master_key
|
||||||
@ -16,7 +17,7 @@ from .utils import decrypt, encrypt, generate_key_id, generate_master_key
|
|||||||
|
|
||||||
class Key(BaseModel):
|
class Key(BaseModel):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, policy, key_usage, customer_master_key_spec, description, tags, region
|
self, policy, key_usage, customer_master_key_spec, description, region
|
||||||
):
|
):
|
||||||
self.id = generate_key_id()
|
self.id = generate_key_id()
|
||||||
self.creation_date = unix_time()
|
self.creation_date = unix_time()
|
||||||
@ -29,7 +30,6 @@ class Key(BaseModel):
|
|||||||
self.account_id = ACCOUNT_ID
|
self.account_id = ACCOUNT_ID
|
||||||
self.key_rotation_status = False
|
self.key_rotation_status = False
|
||||||
self.deletion_date = None
|
self.deletion_date = None
|
||||||
self.tags = tags or {}
|
|
||||||
self.key_material = generate_master_key()
|
self.key_material = generate_master_key()
|
||||||
self.origin = "AWS_KMS"
|
self.origin = "AWS_KMS"
|
||||||
self.key_manager = "CUSTOMER"
|
self.key_manager = "CUSTOMER"
|
||||||
@ -111,11 +111,12 @@ class Key(BaseModel):
|
|||||||
key_usage="ENCRYPT_DECRYPT",
|
key_usage="ENCRYPT_DECRYPT",
|
||||||
customer_master_key_spec="SYMMETRIC_DEFAULT",
|
customer_master_key_spec="SYMMETRIC_DEFAULT",
|
||||||
description=properties["Description"],
|
description=properties["Description"],
|
||||||
tags=properties.get("Tags"),
|
tags=properties.get("Tags", []),
|
||||||
region=region_name,
|
region=region_name,
|
||||||
)
|
)
|
||||||
key.key_rotation_status = properties["EnableKeyRotation"]
|
key.key_rotation_status = properties["EnableKeyRotation"]
|
||||||
key.enabled = properties["Enabled"]
|
key.enabled = properties["Enabled"]
|
||||||
|
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
@ -130,32 +131,26 @@ class KmsBackend(BaseBackend):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.keys = {}
|
self.keys = {}
|
||||||
self.key_to_aliases = defaultdict(set)
|
self.key_to_aliases = defaultdict(set)
|
||||||
|
self.tagger = TaggingService(keyName="TagKey", valueName="TagValue")
|
||||||
|
|
||||||
def create_key(
|
def create_key(
|
||||||
self, policy, key_usage, customer_master_key_spec, description, tags, region
|
self, policy, key_usage, customer_master_key_spec, description, tags, region
|
||||||
):
|
):
|
||||||
key = Key(
|
key = Key(policy, key_usage, customer_master_key_spec, description, region)
|
||||||
policy, key_usage, customer_master_key_spec, description, tags, region
|
|
||||||
)
|
|
||||||
self.keys[key.id] = key
|
self.keys[key.id] = key
|
||||||
|
if tags is not None and len(tags) > 0:
|
||||||
|
self.tag_resource(key.id, tags)
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def update_key_description(self, key_id, description):
|
def update_key_description(self, key_id, description):
|
||||||
key = self.keys[self.get_key_id(key_id)]
|
key = self.keys[self.get_key_id(key_id)]
|
||||||
key.description = description
|
key.description = description
|
||||||
|
|
||||||
def tag_resource(self, key_id, tags):
|
|
||||||
key = self.keys[self.get_key_id(key_id)]
|
|
||||||
key.tags = tags
|
|
||||||
|
|
||||||
def list_resource_tags(self, key_id):
|
|
||||||
key = self.keys[self.get_key_id(key_id)]
|
|
||||||
return key.tags
|
|
||||||
|
|
||||||
def delete_key(self, key_id):
|
def delete_key(self, key_id):
|
||||||
if key_id in self.keys:
|
if key_id in self.keys:
|
||||||
if key_id in self.key_to_aliases:
|
if key_id in self.key_to_aliases:
|
||||||
self.key_to_aliases.pop(key_id)
|
self.key_to_aliases.pop(key_id)
|
||||||
|
self.tagger.delete_all_tags_for_resource(key_id)
|
||||||
|
|
||||||
return self.keys.pop(key_id)
|
return self.keys.pop(key_id)
|
||||||
|
|
||||||
@ -325,6 +320,32 @@ class KmsBackend(BaseBackend):
|
|||||||
|
|
||||||
return plaintext, ciphertext_blob, arn
|
return plaintext, ciphertext_blob, arn
|
||||||
|
|
||||||
|
def list_resource_tags(self, key_id):
|
||||||
|
if key_id in self.keys:
|
||||||
|
return self.tagger.list_tags_for_resource(key_id)
|
||||||
|
raise JsonRESTError(
|
||||||
|
"NotFoundException",
|
||||||
|
"The request was rejected because the specified entity or resource could not be found.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def tag_resource(self, key_id, tags):
|
||||||
|
if key_id in self.keys:
|
||||||
|
self.tagger.tag_resource(key_id, tags)
|
||||||
|
return {}
|
||||||
|
raise JsonRESTError(
|
||||||
|
"NotFoundException",
|
||||||
|
"The request was rejected because the specified entity or resource could not be found.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def untag_resource(self, key_id, tag_names):
|
||||||
|
if key_id in self.keys:
|
||||||
|
self.tagger.untag_resource_using_names(key_id, tag_names)
|
||||||
|
return {}
|
||||||
|
raise JsonRESTError(
|
||||||
|
"NotFoundException",
|
||||||
|
"The request was rejected because the specified entity or resource could not be found.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
kms_backends = {}
|
kms_backends = {}
|
||||||
for region in Session().get_available_regions("kms"):
|
for region in Session().get_available_regions("kms"):
|
||||||
|
@ -144,17 +144,27 @@ class KmsResponse(BaseResponse):
|
|||||||
|
|
||||||
self._validate_cmk_id(key_id)
|
self._validate_cmk_id(key_id)
|
||||||
|
|
||||||
self.kms_backend.tag_resource(key_id, tags)
|
result = self.kms_backend.tag_resource(key_id, tags)
|
||||||
return json.dumps({})
|
return json.dumps(result)
|
||||||
|
|
||||||
|
def untag_resource(self):
|
||||||
|
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_UntagResource.html"""
|
||||||
|
key_id = self.parameters.get("KeyId")
|
||||||
|
tag_names = self.parameters.get("TagKeys")
|
||||||
|
|
||||||
|
self._validate_cmk_id(key_id)
|
||||||
|
|
||||||
|
result = self.kms_backend.untag_resource(key_id, tag_names)
|
||||||
|
return json.dumps(result)
|
||||||
|
|
||||||
def list_resource_tags(self):
|
def list_resource_tags(self):
|
||||||
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_ListResourceTags.html"""
|
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_ListResourceTags.html"""
|
||||||
key_id = self.parameters.get("KeyId")
|
key_id = self.parameters.get("KeyId")
|
||||||
|
|
||||||
self._validate_cmk_id(key_id)
|
self._validate_cmk_id(key_id)
|
||||||
|
|
||||||
tags = self.kms_backend.list_resource_tags(key_id)
|
tags = self.kms_backend.list_resource_tags(key_id)
|
||||||
return json.dumps({"Tags": tags, "NextMarker": None, "Truncated": False})
|
tags.update({"NextMarker": None, "Truncated": False})
|
||||||
|
return json.dumps(tags)
|
||||||
|
|
||||||
def describe_key(self):
|
def describe_key(self):
|
||||||
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html"""
|
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html"""
|
||||||
|
@ -318,7 +318,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
|
|||||||
# KMS
|
# KMS
|
||||||
def get_kms_tags(kms_key_id):
|
def get_kms_tags(kms_key_id):
|
||||||
result = []
|
result = []
|
||||||
for tag in self.kms_backend.list_resource_tags(kms_key_id):
|
for tag in self.kms_backend.list_resource_tags(kms_key_id).get("Tags", []):
|
||||||
result.append({"Key": tag["TagKey"], "Value": tag["TagValue"]})
|
result.append({"Key": tag["TagKey"], "Value": tag["TagValue"]})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ class WorkflowType(GenericType):
|
|||||||
"defaultChildPolicy",
|
"defaultChildPolicy",
|
||||||
"defaultExecutionStartToCloseTimeout",
|
"defaultExecutionStartToCloseTimeout",
|
||||||
"defaultTaskStartToCloseTimeout",
|
"defaultTaskStartToCloseTimeout",
|
||||||
|
"defaultTaskPriority",
|
||||||
|
"defaultLambdaRole",
|
||||||
]
|
]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -300,6 +300,8 @@ class SWFResponse(BaseResponse):
|
|||||||
default_execution_start_to_close_timeout = self._params.get(
|
default_execution_start_to_close_timeout = self._params.get(
|
||||||
"defaultExecutionStartToCloseTimeout"
|
"defaultExecutionStartToCloseTimeout"
|
||||||
)
|
)
|
||||||
|
default_task_priority = self._params.get("defaultTaskPriority")
|
||||||
|
default_lambda_role = self._params.get("defaultLambdaRole")
|
||||||
description = self._params.get("description")
|
description = self._params.get("description")
|
||||||
|
|
||||||
self._check_string(domain)
|
self._check_string(domain)
|
||||||
@ -309,10 +311,10 @@ class SWFResponse(BaseResponse):
|
|||||||
self._check_none_or_string(default_child_policy)
|
self._check_none_or_string(default_child_policy)
|
||||||
self._check_none_or_string(default_task_start_to_close_timeout)
|
self._check_none_or_string(default_task_start_to_close_timeout)
|
||||||
self._check_none_or_string(default_execution_start_to_close_timeout)
|
self._check_none_or_string(default_execution_start_to_close_timeout)
|
||||||
|
self._check_none_or_string(default_task_priority)
|
||||||
|
self._check_none_or_string(default_lambda_role)
|
||||||
self._check_none_or_string(description)
|
self._check_none_or_string(description)
|
||||||
|
|
||||||
# TODO: add defaultTaskPriority when boto gets to support it
|
|
||||||
# TODO: add defaultLambdaRole when boto gets to support it
|
|
||||||
self.swf_backend.register_type(
|
self.swf_backend.register_type(
|
||||||
"workflow",
|
"workflow",
|
||||||
domain,
|
domain,
|
||||||
@ -322,6 +324,8 @@ class SWFResponse(BaseResponse):
|
|||||||
default_child_policy=default_child_policy,
|
default_child_policy=default_child_policy,
|
||||||
default_task_start_to_close_timeout=default_task_start_to_close_timeout,
|
default_task_start_to_close_timeout=default_task_start_to_close_timeout,
|
||||||
default_execution_start_to_close_timeout=default_execution_start_to_close_timeout,
|
default_execution_start_to_close_timeout=default_execution_start_to_close_timeout,
|
||||||
|
default_task_priority=default_task_priority,
|
||||||
|
default_lambda_role=default_lambda_role,
|
||||||
description=description,
|
description=description,
|
||||||
)
|
)
|
||||||
return ""
|
return ""
|
||||||
|
@ -8,7 +8,7 @@ import sure # noqa
|
|||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
|
|
||||||
import responses
|
import responses
|
||||||
from moto import mock_apigateway, settings
|
from moto import mock_apigateway, mock_cognitoidp, settings
|
||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
from nose.tools import assert_raises
|
from nose.tools import assert_raises
|
||||||
|
|
||||||
@ -576,6 +576,254 @@ def test_integration_response():
|
|||||||
response["methodIntegration"]["integrationResponses"].should.equal({})
|
response["methodIntegration"]["integrationResponses"].should.equal({})
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_update_authorizer_configuration():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
authorizer_name = "my_authorizer"
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
cognito_client = boto3.client("cognito-idp", region_name="us-west-2")
|
||||||
|
user_pool_arn = cognito_client.create_user_pool(PoolName="my_cognito_pool")[
|
||||||
|
"UserPool"
|
||||||
|
]["Arn"]
|
||||||
|
|
||||||
|
response = client.create_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
name=authorizer_name,
|
||||||
|
type="COGNITO_USER_POOLS",
|
||||||
|
providerARNs=[user_pool_arn],
|
||||||
|
identitySource="method.request.header.Authorization",
|
||||||
|
)
|
||||||
|
authorizer_id = response["id"]
|
||||||
|
|
||||||
|
response = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
|
||||||
|
# createdDate is hard to match against, remove it
|
||||||
|
response.pop("createdDate", None)
|
||||||
|
# this is hard to match against, so remove it
|
||||||
|
response["ResponseMetadata"].pop("HTTPHeaders", None)
|
||||||
|
response["ResponseMetadata"].pop("RetryAttempts", None)
|
||||||
|
response.should.equal(
|
||||||
|
{
|
||||||
|
"id": authorizer_id,
|
||||||
|
"name": authorizer_name,
|
||||||
|
"type": "COGNITO_USER_POOLS",
|
||||||
|
"providerARNs": [user_pool_arn],
|
||||||
|
"identitySource": "method.request.header.Authorization",
|
||||||
|
"authorizerResultTtlInSeconds": 300,
|
||||||
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
client.update_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
authorizerId=authorizer_id,
|
||||||
|
patchOperations=[{"op": "replace", "path": "/type", "value": "TOKEN"}],
|
||||||
|
)
|
||||||
|
|
||||||
|
authorizer = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
|
||||||
|
|
||||||
|
authorizer.should.have.key("type").which.should.equal("TOKEN")
|
||||||
|
|
||||||
|
client.update_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
authorizerId=authorizer_id,
|
||||||
|
patchOperations=[{"op": "replace", "path": "/type", "value": "REQUEST"}],
|
||||||
|
)
|
||||||
|
|
||||||
|
authorizer = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
|
||||||
|
|
||||||
|
authorizer.should.have.key("type").which.should.equal("REQUEST")
|
||||||
|
|
||||||
|
# TODO: implement mult-update tests
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.update_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
authorizerId=authorizer_id,
|
||||||
|
patchOperations=[
|
||||||
|
{"op": "add", "path": "/notasetting", "value": "eu-west-1"}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert False.should.be.ok # Fail, should not be here
|
||||||
|
except Exception:
|
||||||
|
assert True.should.be.ok
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_non_existent_authorizer():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
client.get_authorizer.when.called_with(
|
||||||
|
restApiId=api_id, authorizerId="xxx"
|
||||||
|
).should.throw(ClientError)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_create_authorizer():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
authorizer_name = "my_authorizer"
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
cognito_client = boto3.client("cognito-idp", region_name="us-west-2")
|
||||||
|
user_pool_arn = cognito_client.create_user_pool(PoolName="my_cognito_pool")[
|
||||||
|
"UserPool"
|
||||||
|
]["Arn"]
|
||||||
|
|
||||||
|
response = client.create_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
name=authorizer_name,
|
||||||
|
type="COGNITO_USER_POOLS",
|
||||||
|
providerARNs=[user_pool_arn],
|
||||||
|
identitySource="method.request.header.Authorization",
|
||||||
|
)
|
||||||
|
authorizer_id = response["id"]
|
||||||
|
|
||||||
|
response = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
|
||||||
|
# createdDate is hard to match against, remove it
|
||||||
|
response.pop("createdDate", None)
|
||||||
|
# this is hard to match against, so remove it
|
||||||
|
response["ResponseMetadata"].pop("HTTPHeaders", None)
|
||||||
|
response["ResponseMetadata"].pop("RetryAttempts", None)
|
||||||
|
response.should.equal(
|
||||||
|
{
|
||||||
|
"id": authorizer_id,
|
||||||
|
"name": authorizer_name,
|
||||||
|
"type": "COGNITO_USER_POOLS",
|
||||||
|
"providerARNs": [user_pool_arn],
|
||||||
|
"identitySource": "method.request.header.Authorization",
|
||||||
|
"authorizerResultTtlInSeconds": 300,
|
||||||
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
authorizer_name2 = "my_authorizer2"
|
||||||
|
response = client.create_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
name=authorizer_name2,
|
||||||
|
type="COGNITO_USER_POOLS",
|
||||||
|
providerARNs=[user_pool_arn],
|
||||||
|
identitySource="method.request.header.Authorization",
|
||||||
|
)
|
||||||
|
authorizer_id2 = response["id"]
|
||||||
|
|
||||||
|
response = client.get_authorizers(restApiId=api_id)
|
||||||
|
|
||||||
|
# this is hard to match against, so remove it
|
||||||
|
response["ResponseMetadata"].pop("HTTPHeaders", None)
|
||||||
|
response["ResponseMetadata"].pop("RetryAttempts", None)
|
||||||
|
|
||||||
|
response["items"][0]["id"].should.match(
|
||||||
|
r"{0}|{1}".format(authorizer_id2, authorizer_id)
|
||||||
|
)
|
||||||
|
response["items"][1]["id"].should.match(
|
||||||
|
r"{0}|{1}".format(authorizer_id2, authorizer_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
new_authorizer_name_with_vars = "authorizer_with_vars"
|
||||||
|
response = client.create_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
name=new_authorizer_name_with_vars,
|
||||||
|
type="COGNITO_USER_POOLS",
|
||||||
|
providerARNs=[user_pool_arn],
|
||||||
|
identitySource="method.request.header.Authorization",
|
||||||
|
)
|
||||||
|
authorizer_id3 = response["id"]
|
||||||
|
|
||||||
|
# this is hard to match against, so remove it
|
||||||
|
response["ResponseMetadata"].pop("HTTPHeaders", None)
|
||||||
|
response["ResponseMetadata"].pop("RetryAttempts", None)
|
||||||
|
|
||||||
|
response.should.equal(
|
||||||
|
{
|
||||||
|
"name": new_authorizer_name_with_vars,
|
||||||
|
"id": authorizer_id3,
|
||||||
|
"type": "COGNITO_USER_POOLS",
|
||||||
|
"providerARNs": [user_pool_arn],
|
||||||
|
"identitySource": "method.request.header.Authorization",
|
||||||
|
"authorizerResultTtlInSeconds": 300,
|
||||||
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
stage = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id3)
|
||||||
|
stage["name"].should.equal(new_authorizer_name_with_vars)
|
||||||
|
stage["id"].should.equal(authorizer_id3)
|
||||||
|
stage["type"].should.equal("COGNITO_USER_POOLS")
|
||||||
|
stage["providerARNs"].should.equal([user_pool_arn])
|
||||||
|
stage["identitySource"].should.equal("method.request.header.Authorization")
|
||||||
|
stage["authorizerResultTtlInSeconds"].should.equal(300)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
@mock_cognitoidp
|
||||||
|
def test_delete_authorizer():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
authorizer_name = "my_authorizer"
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
cognito_client = boto3.client("cognito-idp", region_name="us-west-2")
|
||||||
|
user_pool_arn = cognito_client.create_user_pool(PoolName="my_cognito_pool")[
|
||||||
|
"UserPool"
|
||||||
|
]["Arn"]
|
||||||
|
|
||||||
|
response = client.create_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
name=authorizer_name,
|
||||||
|
type="COGNITO_USER_POOLS",
|
||||||
|
providerARNs=[user_pool_arn],
|
||||||
|
identitySource="method.request.header.Authorization",
|
||||||
|
)
|
||||||
|
authorizer_id = response["id"]
|
||||||
|
|
||||||
|
response = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
|
||||||
|
# createdDate is hard to match against, remove it
|
||||||
|
response.pop("createdDate", None)
|
||||||
|
# this is hard to match against, so remove it
|
||||||
|
response["ResponseMetadata"].pop("HTTPHeaders", None)
|
||||||
|
response["ResponseMetadata"].pop("RetryAttempts", None)
|
||||||
|
response.should.equal(
|
||||||
|
{
|
||||||
|
"id": authorizer_id,
|
||||||
|
"name": authorizer_name,
|
||||||
|
"type": "COGNITO_USER_POOLS",
|
||||||
|
"providerARNs": [user_pool_arn],
|
||||||
|
"identitySource": "method.request.header.Authorization",
|
||||||
|
"authorizerResultTtlInSeconds": 300,
|
||||||
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
authorizer_name2 = "my_authorizer2"
|
||||||
|
response = client.create_authorizer(
|
||||||
|
restApiId=api_id,
|
||||||
|
name=authorizer_name2,
|
||||||
|
type="COGNITO_USER_POOLS",
|
||||||
|
providerARNs=[user_pool_arn],
|
||||||
|
identitySource="method.request.header.Authorization",
|
||||||
|
)
|
||||||
|
authorizer_id2 = response["id"]
|
||||||
|
|
||||||
|
authorizers = client.get_authorizers(restApiId=api_id)["items"]
|
||||||
|
sorted([authorizer["name"] for authorizer in authorizers]).should.equal(
|
||||||
|
sorted([authorizer_name2, authorizer_name])
|
||||||
|
)
|
||||||
|
# delete stage
|
||||||
|
response = client.delete_authorizer(restApiId=api_id, authorizerId=authorizer_id2)
|
||||||
|
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(202)
|
||||||
|
# verify other stage still exists
|
||||||
|
authorizers = client.get_authorizers(restApiId=api_id)["items"]
|
||||||
|
sorted([authorizer["name"] for authorizer in authorizers]).should.equal(
|
||||||
|
sorted([authorizer_name])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@mock_apigateway
|
@mock_apigateway
|
||||||
def test_update_stage_configuration():
|
def test_update_stage_configuration():
|
||||||
client = boto3.client("apigateway", region_name="us-west-2")
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
@ -1161,7 +1161,7 @@ def test_invoke_function_from_sqs():
|
|||||||
@mock_logs
|
@mock_logs
|
||||||
@mock_lambda
|
@mock_lambda
|
||||||
@mock_dynamodb2
|
@mock_dynamodb2
|
||||||
def test_invoke_function_from_dynamodb():
|
def test_invoke_function_from_dynamodb_put():
|
||||||
logs_conn = boto3.client("logs", region_name="us-east-1")
|
logs_conn = boto3.client("logs", region_name="us-east-1")
|
||||||
dynamodb = boto3.client("dynamodb", region_name="us-east-1")
|
dynamodb = boto3.client("dynamodb", region_name="us-east-1")
|
||||||
table_name = "table_with_stream"
|
table_name = "table_with_stream"
|
||||||
@ -1218,6 +1218,72 @@ def test_invoke_function_from_dynamodb():
|
|||||||
assert False, "Test Failed"
|
assert False, "Test Failed"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_logs
|
||||||
|
@mock_lambda
|
||||||
|
@mock_dynamodb2
|
||||||
|
def test_invoke_function_from_dynamodb_update():
|
||||||
|
logs_conn = boto3.client("logs", region_name="us-east-1")
|
||||||
|
dynamodb = boto3.client("dynamodb", region_name="us-east-1")
|
||||||
|
table_name = "table_with_stream"
|
||||||
|
table = dynamodb.create_table(
|
||||||
|
TableName=table_name,
|
||||||
|
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||||
|
AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
|
||||||
|
StreamSpecification={
|
||||||
|
"StreamEnabled": True,
|
||||||
|
"StreamViewType": "NEW_AND_OLD_IMAGES",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
dynamodb.put_item(TableName=table_name, Item={"id": {"S": "item 1"}})
|
||||||
|
|
||||||
|
conn = boto3.client("lambda", region_name="us-east-1")
|
||||||
|
func = conn.create_function(
|
||||||
|
FunctionName="testFunction",
|
||||||
|
Runtime="python2.7",
|
||||||
|
Role=get_role_name(),
|
||||||
|
Handler="lambda_function.lambda_handler",
|
||||||
|
Code={"ZipFile": get_test_zip_file3()},
|
||||||
|
Description="test lambda function executed after a DynamoDB table is updated",
|
||||||
|
Timeout=3,
|
||||||
|
MemorySize=128,
|
||||||
|
Publish=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = conn.create_event_source_mapping(
|
||||||
|
EventSourceArn=table["TableDescription"]["LatestStreamArn"],
|
||||||
|
FunctionName=func["FunctionArn"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response["EventSourceArn"] == table["TableDescription"]["LatestStreamArn"]
|
||||||
|
assert response["State"] == "Enabled"
|
||||||
|
dynamodb.update_item(
|
||||||
|
TableName=table_name,
|
||||||
|
Key={"id": {"S": "item 1"}},
|
||||||
|
UpdateExpression="set #attr = :val",
|
||||||
|
ExpressionAttributeNames={"#attr": "new_attr"},
|
||||||
|
ExpressionAttributeValues={":val": {"S": "new_val"}},
|
||||||
|
)
|
||||||
|
start = time.time()
|
||||||
|
while (time.time() - start) < 30:
|
||||||
|
result = logs_conn.describe_log_streams(logGroupName="/aws/lambda/testFunction")
|
||||||
|
log_streams = result.get("logStreams")
|
||||||
|
if not log_streams:
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
|
||||||
|
assert len(log_streams) == 1
|
||||||
|
result = logs_conn.get_log_events(
|
||||||
|
logGroupName="/aws/lambda/testFunction",
|
||||||
|
logStreamName=log_streams[0]["logStreamName"],
|
||||||
|
)
|
||||||
|
for event in result.get("events"):
|
||||||
|
if event["message"] == "get_test_zip_file3 success":
|
||||||
|
return
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
assert False, "Test Failed"
|
||||||
|
|
||||||
|
|
||||||
@mock_logs
|
@mock_logs
|
||||||
@mock_lambda
|
@mock_lambda
|
||||||
@mock_sqs
|
@mock_sqs
|
||||||
|
@ -752,7 +752,9 @@ def test_steps():
|
|||||||
# StateChangeReason
|
# StateChangeReason
|
||||||
x["Status"]["Timeline"]["CreationDateTime"].should.be.a("datetime.datetime")
|
x["Status"]["Timeline"]["CreationDateTime"].should.be.a("datetime.datetime")
|
||||||
# x['Status']['Timeline']['EndDateTime'].should.be.a('datetime.datetime')
|
# x['Status']['Timeline']['EndDateTime'].should.be.a('datetime.datetime')
|
||||||
# x['Status']['Timeline']['StartDateTime'].should.be.a('datetime.datetime')
|
# Only the first step will have started - we don't know anything about when it finishes, so the second step never starts
|
||||||
|
if x["Name"] == "My wordcount example":
|
||||||
|
x["Status"]["Timeline"]["StartDateTime"].should.be.a("datetime.datetime")
|
||||||
|
|
||||||
x = client.describe_step(ClusterId=cluster_id, StepId=x["Id"])["Step"]
|
x = client.describe_step(ClusterId=cluster_id, StepId=x["Id"])["Step"]
|
||||||
x["ActionOnFailure"].should.equal("TERMINATE_CLUSTER")
|
x["ActionOnFailure"].should.equal("TERMINATE_CLUSTER")
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
|
from moto.events.models import EventsBackend
|
||||||
|
from moto.events import mock_events
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
|
from moto.core.exceptions import JsonRESTError
|
||||||
from nose.tools import assert_raises
|
from nose.tools import assert_raises
|
||||||
|
|
||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
from moto.core.exceptions import JsonRESTError
|
|
||||||
from moto.events import mock_events
|
|
||||||
from moto.events.models import EventsBackend
|
|
||||||
|
|
||||||
RULES = [
|
RULES = [
|
||||||
{"Name": "test1", "ScheduleExpression": "rate(5 minutes)"},
|
{"Name": "test1", "ScheduleExpression": "rate(5 minutes)"},
|
||||||
|
@ -4,15 +4,17 @@ import base64
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import boto.kms
|
import boto.kms
|
||||||
|
import boto3
|
||||||
import six
|
import six
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
from boto.exception import JSONResponseError
|
from boto.exception import JSONResponseError
|
||||||
from boto.kms.exceptions import AlreadyExistsException, NotFoundException
|
from boto.kms.exceptions import AlreadyExistsException, NotFoundException
|
||||||
from nose.tools import assert_raises
|
from nose.tools import assert_raises
|
||||||
from parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
from moto.core.exceptions import JsonRESTError
|
||||||
|
from moto.kms.models import KmsBackend
|
||||||
from moto.kms.exceptions import NotFoundException as MotoNotFoundException
|
from moto.kms.exceptions import NotFoundException as MotoNotFoundException
|
||||||
from moto import mock_kms_deprecated
|
from moto import mock_kms_deprecated, mock_kms
|
||||||
|
|
||||||
PLAINTEXT_VECTORS = (
|
PLAINTEXT_VECTORS = (
|
||||||
(b"some encodeable plaintext",),
|
(b"some encodeable plaintext",),
|
||||||
@ -679,3 +681,77 @@ def test__assert_default_policy():
|
|||||||
_assert_default_policy.when.called_with("default").should_not.throw(
|
_assert_default_policy.when.called_with("default").should_not.throw(
|
||||||
MotoNotFoundException
|
MotoNotFoundException
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if six.PY2:
|
||||||
|
sort = sorted
|
||||||
|
else:
|
||||||
|
sort = lambda l: sorted(l, key=lambda d: d.keys())
|
||||||
|
|
||||||
|
|
||||||
|
@mock_kms
|
||||||
|
def test_key_tag_on_create_key_happy():
|
||||||
|
client = boto3.client("kms", region_name="us-east-1")
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
{"TagKey": "key1", "TagValue": "value1"},
|
||||||
|
{"TagKey": "key2", "TagValue": "value2"},
|
||||||
|
]
|
||||||
|
key = client.create_key(Description="test-key-tagging", Tags=tags)
|
||||||
|
key_id = key["KeyMetadata"]["KeyId"]
|
||||||
|
|
||||||
|
result = client.list_resource_tags(KeyId=key_id)
|
||||||
|
actual = result.get("Tags", [])
|
||||||
|
assert sort(tags) == sort(actual)
|
||||||
|
|
||||||
|
client.untag_resource(KeyId=key_id, TagKeys=["key1"])
|
||||||
|
|
||||||
|
actual = client.list_resource_tags(KeyId=key_id).get("Tags", [])
|
||||||
|
expected = [{"TagKey": "key2", "TagValue": "value2"}]
|
||||||
|
assert sort(expected) == sort(actual)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_kms
|
||||||
|
def test_key_tag_added_happy():
|
||||||
|
client = boto3.client("kms", region_name="us-east-1")
|
||||||
|
|
||||||
|
key = client.create_key(Description="test-key-tagging")
|
||||||
|
key_id = key["KeyMetadata"]["KeyId"]
|
||||||
|
tags = [
|
||||||
|
{"TagKey": "key1", "TagValue": "value1"},
|
||||||
|
{"TagKey": "key2", "TagValue": "value2"},
|
||||||
|
]
|
||||||
|
client.tag_resource(KeyId=key_id, Tags=tags)
|
||||||
|
|
||||||
|
result = client.list_resource_tags(KeyId=key_id)
|
||||||
|
actual = result.get("Tags", [])
|
||||||
|
assert sort(tags) == sort(actual)
|
||||||
|
|
||||||
|
client.untag_resource(KeyId=key_id, TagKeys=["key1"])
|
||||||
|
|
||||||
|
actual = client.list_resource_tags(KeyId=key_id).get("Tags", [])
|
||||||
|
expected = [{"TagKey": "key2", "TagValue": "value2"}]
|
||||||
|
assert sort(expected) == sort(actual)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_kms_deprecated
|
||||||
|
def test_key_tagging_sad():
|
||||||
|
b = KmsBackend()
|
||||||
|
|
||||||
|
try:
|
||||||
|
b.tag_resource("unknown", [])
|
||||||
|
raise "tag_resource should fail if KeyId is not known"
|
||||||
|
except JsonRESTError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
b.untag_resource("unknown", [])
|
||||||
|
raise "untag_resource should fail if KeyId is not known"
|
||||||
|
except JsonRESTError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
b.list_resource_tags("unknown")
|
||||||
|
raise "list_resource_tags should fail if KeyId is not known"
|
||||||
|
except JsonRESTError:
|
||||||
|
pass
|
||||||
|
@ -102,7 +102,7 @@ def test_deserialize_ciphertext_blob(raw, serialized):
|
|||||||
@parameterized(((ec[0],) for ec in ENCRYPTION_CONTEXT_VECTORS))
|
@parameterized(((ec[0],) for ec in ENCRYPTION_CONTEXT_VECTORS))
|
||||||
def test_encrypt_decrypt_cycle(encryption_context):
|
def test_encrypt_decrypt_cycle(encryption_context):
|
||||||
plaintext = b"some secret plaintext"
|
plaintext = b"some secret plaintext"
|
||||||
master_key = Key("nop", "nop", "nop", "nop", [], "nop")
|
master_key = Key("nop", "nop", "nop", "nop", "nop")
|
||||||
master_key_map = {master_key.id: master_key}
|
master_key_map = {master_key.id: master_key}
|
||||||
|
|
||||||
ciphertext_blob = encrypt(
|
ciphertext_blob = encrypt(
|
||||||
@ -133,7 +133,7 @@ def test_encrypt_unknown_key_id():
|
|||||||
|
|
||||||
|
|
||||||
def test_decrypt_invalid_ciphertext_format():
|
def test_decrypt_invalid_ciphertext_format():
|
||||||
master_key = Key("nop", "nop", "nop", "nop", [], "nop")
|
master_key = Key("nop", "nop", "nop", "nop", "nop")
|
||||||
master_key_map = {master_key.id: master_key}
|
master_key_map = {master_key.id: master_key}
|
||||||
|
|
||||||
with assert_raises(InvalidCiphertextException):
|
with assert_raises(InvalidCiphertextException):
|
||||||
@ -153,7 +153,7 @@ def test_decrypt_unknwown_key_id():
|
|||||||
|
|
||||||
|
|
||||||
def test_decrypt_invalid_ciphertext():
|
def test_decrypt_invalid_ciphertext():
|
||||||
master_key = Key("nop", "nop", "nop", "nop", [], "nop")
|
master_key = Key("nop", "nop", "nop", "nop", "nop")
|
||||||
master_key_map = {master_key.id: master_key}
|
master_key_map = {master_key.id: master_key}
|
||||||
ciphertext_blob = (
|
ciphertext_blob = (
|
||||||
master_key.id.encode("utf-8") + b"123456789012"
|
master_key.id.encode("utf-8") + b"123456789012"
|
||||||
@ -171,7 +171,7 @@ def test_decrypt_invalid_ciphertext():
|
|||||||
|
|
||||||
def test_decrypt_invalid_encryption_context():
|
def test_decrypt_invalid_encryption_context():
|
||||||
plaintext = b"some secret plaintext"
|
plaintext = b"some secret plaintext"
|
||||||
master_key = Key("nop", "nop", "nop", "nop", [], "nop")
|
master_key = Key("nop", "nop", "nop", "nop", "nop")
|
||||||
master_key_map = {master_key.id: master_key}
|
master_key_map = {master_key.id: master_key}
|
||||||
|
|
||||||
ciphertext_blob = encrypt(
|
ciphertext_blob = encrypt(
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import sure
|
import sure
|
||||||
import boto
|
import boto
|
||||||
|
import boto3
|
||||||
|
|
||||||
from moto import mock_swf_deprecated
|
from moto import mock_swf_deprecated
|
||||||
|
from moto import mock_swf
|
||||||
from boto.swf.exceptions import SWFResponseError
|
from boto.swf.exceptions import SWFResponseError
|
||||||
|
|
||||||
|
|
||||||
@ -133,6 +135,41 @@ def test_describe_workflow_type():
|
|||||||
infos["status"].should.equal("REGISTERED")
|
infos["status"].should.equal("REGISTERED")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_swf
|
||||||
|
def test_describe_workflow_type_full_boto3():
|
||||||
|
# boto3 required as boto doesn't support all of the arguments
|
||||||
|
client = boto3.client("swf", region_name="us-east-1")
|
||||||
|
client.register_domain(
|
||||||
|
name="test-domain", workflowExecutionRetentionPeriodInDays="2"
|
||||||
|
)
|
||||||
|
client.register_workflow_type(
|
||||||
|
domain="test-domain",
|
||||||
|
name="test-workflow",
|
||||||
|
version="v1.0",
|
||||||
|
description="Test workflow.",
|
||||||
|
defaultTaskStartToCloseTimeout="20",
|
||||||
|
defaultExecutionStartToCloseTimeout="60",
|
||||||
|
defaultTaskList={"name": "foo"},
|
||||||
|
defaultTaskPriority="-2",
|
||||||
|
defaultChildPolicy="ABANDON",
|
||||||
|
defaultLambdaRole="arn:bar",
|
||||||
|
)
|
||||||
|
|
||||||
|
resp = client.describe_workflow_type(
|
||||||
|
domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
|
||||||
|
)
|
||||||
|
resp["typeInfo"]["workflowType"]["name"].should.equal("test-workflow")
|
||||||
|
resp["typeInfo"]["workflowType"]["version"].should.equal("v1.0")
|
||||||
|
resp["typeInfo"]["status"].should.equal("REGISTERED")
|
||||||
|
resp["typeInfo"]["description"].should.equal("Test workflow.")
|
||||||
|
resp["configuration"]["defaultTaskStartToCloseTimeout"].should.equal("20")
|
||||||
|
resp["configuration"]["defaultExecutionStartToCloseTimeout"].should.equal("60")
|
||||||
|
resp["configuration"]["defaultTaskList"]["name"].should.equal("foo")
|
||||||
|
resp["configuration"]["defaultTaskPriority"].should.equal("-2")
|
||||||
|
resp["configuration"]["defaultChildPolicy"].should.equal("ABANDON")
|
||||||
|
resp["configuration"]["defaultLambdaRole"].should.equal("arn:bar")
|
||||||
|
|
||||||
|
|
||||||
@mock_swf_deprecated
|
@mock_swf_deprecated
|
||||||
def test_describe_non_existent_workflow_type():
|
def test_describe_non_existent_workflow_type():
|
||||||
conn = boto.connect_swf("the_key", "the_secret")
|
conn = boto.connect_swf("the_key", "the_secret")
|
||||||
|
Loading…
Reference in New Issue
Block a user