MultiAccount support (#5192)

This commit is contained in:
Bert Blommers 2022-08-13 09:49:43 +00:00 committed by GitHub
parent 50f15b0c0b
commit 3d913f8f15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
431 changed files with 4243 additions and 2841 deletions

View File

@ -2577,7 +2577,7 @@
- [ ] list_job_runs - [ ] list_job_runs
- [ ] list_tags_for_resource - [ ] list_tags_for_resource
- [X] start_application - [X] start_application
- [ ] start_job_run - [X] start_job_run
- [X] stop_application - [X] stop_application
- [ ] tag_resource - [ ] tag_resource
- [ ] untag_resource - [ ] untag_resource

View File

@ -1,7 +1,7 @@
codecov: codecov:
notify: notify:
# Leave a GitHub comment after all builds have passed # Leave a GitHub comment after all builds have passed
after_n_builds: 8 after_n_builds: 10
coverage: coverage:
status: status:
project: project:

133
docs/docs/multi_account.rst Normal file
View File

@ -0,0 +1,133 @@
.. _multi_account:
=====================
Multi-Account support
=====================
By default, Moto processes all requests in a default account: `12345678910`. The exact credentials provided are usually ignored to make the process of mocking requests as hassle-free as possible.
If you want to mock resources in multiple accounts, or you want to change the default account ID, there are multiple ways to achieve this.
Configure the default account
------------------------------
It is possible to configure the default account ID that will be used for all incoming requests, by setting the environment variable `MOTO_ACCOUNT_ID`.
Here is an example of what this looks like in practice:
.. sourcecode:: python
# Create a bucket in the default account
client = boto3.client("s3", region_name="us-east-1")
client.create_bucket(Bucket="bucket-default-account")
# Configure another account - all subsequent requests will use this account ID
os.environ["MOTO_ACCOUNT_ID"] = "111111111111"
client.create_bucket(Bucket="bucket-in-account-2")
assert [b["Name"] for b in client2.list_buckets()["Buckets"]] == ["bucket-in-account-2"]
# Now revert to the default account, by removing the environment variable
del os.environ["MOTO_ACCOUNT_ID"]
assert [b["Name"] for b in client2.list_buckets()["Buckets"]] == ["bucket-default-account"]
Configure the account ID using a request header
---------------------------------------------------
If you are using Moto in ServerMode you can add a custom header to a request, to specify which account should be used.
.. note::
Moto will only look at the request-header if the environment variable is not set.
As an example, this is how you would create an S3-bucket in another account:
.. sourcecode:: python
headers ={"x-moto-account-id": "333344445555"}
requests.put("http://bucket.localhost:5000/", headers=headers)
# This will return a list of all buckets in account 333344445555
requests.get("http://localhost:5000", headers=headers)
# This will return an empty list, as there are no buckets in the default account
requests.get("http://localhost:5000")
Configure an account using STS
------------------------------
The `STS.assume_role()`-feature is useful if you want to temporarily use a different set of access credentials.
Passing in a role that belongs to a different account will return a set of credentials that give access to that account.
.. note::
To avoid any chicken-and-egg problems trying to create roles in non-existing accounts, these Roles do not need to exist.
Moto will only extract the account ID from the role, and create access credentials for that account.
.. note::
Moto will only look at the access credentials if the environment variable and request header is not set.
Let's look at some examples.
.. sourcecode:: python
# Create a bucket using the default access credentials
client1 = boto3.client("s3", region_name="us-east-1")
client1.create_bucket(Bucket="foobar")
# Assume a role in our account
# Note that this Role does not need to exist
default_account = "123456789012"
sts = boto3.client("sts")
response = sts.assume_role(
RoleArn=f"arn:aws:iam::{default_account}:role/my-role",
RoleSessionName="test-session-name",
ExternalId="test-external-id",
)
# These access credentials give access to the default account
client2 = boto3.client(
"s3",
aws_access_key_id=response["Credentials"]["AccessKeyId"],
aws_secret_access_key=response["Credentials"]["SecretAccessKey"],
aws_session_token=response["Credentials"]["SessionToken"],
region_name="us-east-1",
)
client2.list_buckets()["Buckets"].should.have.length_of(1)
Because we assumed a role within the same account, we can see the bucket that we've just created.
Things get interesting when assuming a role within a different account.
.. sourcecode:: python
# Create a bucket with default access credentials
client1 = boto3.client("s3", region_name="us-east-1")
client1.create_bucket(Bucket="foobar")
# Assume a role in a different account
# Note that the Role does not need to exist
sts = boto3.client("sts")
response = sts.assume_role(
RoleArn="arn:aws:iam::111111111111:role/role-in-another-account",
RoleSessionName="test-session-name",
ExternalId="test-external-id",
)
# Retrieve all buckets in this new account - this will be completely empty
client2 = boto3.client(
"s3",
aws_access_key_id=response["Credentials"]["AccessKeyId"],
aws_secret_access_key=response["Credentials"]["SecretAccessKey"],
aws_session_token=response["Credentials"]["SessionToken"],
region_name="us-east-1",
)
client2.list_buckets()["Buckets"].should.have.length_of(0)
Because we've assumed a role in a different account, no buckets were found. The `foobar`-bucket only exists in the default account, not in `111111111111`.

View File

@ -35,6 +35,7 @@ Additional Resources
docs/faq docs/faq
docs/iam docs/iam
docs/aws_config docs/aws_config
docs/multi_account
.. toctree:: .. toctree::
:hidden: :hidden:

View File

@ -13,8 +13,6 @@ import cryptography.hazmat.primitives.asymmetric.rsa
from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from moto.core import get_account_id
AWS_ROOT_CA = b"""-----BEGIN CERTIFICATE----- AWS_ROOT_CA = b"""-----BEGIN CERTIFICATE-----
MIIESTCCAzGgAwIBAgITBntQXCplJ7wevi2i0ZmY7bibLDANBgkqhkiG9w0BAQsF MIIESTCCAzGgAwIBAgITBntQXCplJ7wevi2i0ZmY7bibLDANBgkqhkiG9w0BAQsF
@ -123,6 +121,7 @@ class TagHolder(dict):
class CertBundle(BaseModel): class CertBundle(BaseModel):
def __init__( def __init__(
self, self,
account_id,
certificate, certificate,
private_key, private_key,
chain=None, chain=None,
@ -161,12 +160,12 @@ class CertBundle(BaseModel):
# Used for when one wants to overwrite an arn # Used for when one wants to overwrite an arn
if arn is None: if arn is None:
self.arn = make_arn_for_certificate(get_account_id(), region) self.arn = make_arn_for_certificate(account_id, region)
else: else:
self.arn = arn self.arn = arn
@classmethod @classmethod
def generate_cert(cls, domain_name, region, sans=None): def generate_cert(cls, domain_name, account_id, region, sans=None):
if sans is None: if sans is None:
sans = set() sans = set()
else: else:
@ -235,10 +234,11 @@ class CertBundle(BaseModel):
) )
return cls( return cls(
cert_armored, certificate=cert_armored,
private_key, private_key=private_key,
cert_type="AMAZON_ISSUED", cert_type="AMAZON_ISSUED",
cert_status="PENDING_VALIDATION", cert_status="PENDING_VALIDATION",
account_id=account_id,
region=region, region=region,
) )
@ -435,11 +435,8 @@ class AWSCertificateManagerBackend(BaseBackend):
service_region, zones, "acm-pca" service_region, zones, "acm-pca"
) )
@staticmethod def _arn_not_found(self, arn):
def _arn_not_found(arn): msg = f"Certificate with arn {arn} not found in account {self.account_id}"
msg = "Certificate with arn {0} not found in account {1}".format(
arn, get_account_id()
)
return AWSResourceNotFoundException(msg) return AWSResourceNotFoundException(msg)
def set_certificate_in_use_by(self, arn, load_balancer_name): def set_certificate_in_use_by(self, arn, load_balancer_name):
@ -485,6 +482,7 @@ class AWSCertificateManagerBackend(BaseBackend):
else: else:
# Will reuse provided ARN # Will reuse provided ARN
bundle = CertBundle( bundle = CertBundle(
self.account_id,
certificate, certificate,
private_key, private_key,
chain=chain, chain=chain,
@ -494,7 +492,11 @@ class AWSCertificateManagerBackend(BaseBackend):
else: else:
# Will generate a random ARN # Will generate a random ARN
bundle = CertBundle( bundle = CertBundle(
certificate, private_key, chain=chain, region=self.region_name self.account_id,
certificate,
private_key,
chain=chain,
region=self.region_name,
) )
self._certificates[bundle.arn] = bundle self._certificates[bundle.arn] = bundle
@ -546,7 +548,10 @@ class AWSCertificateManagerBackend(BaseBackend):
return arn return arn
cert = CertBundle.generate_cert( cert = CertBundle.generate_cert(
domain_name, region=self.region_name, sans=subject_alt_names domain_name,
account_id=self.account_id,
region=self.region_name,
sans=subject_alt_names,
) )
if idempotency_token is not None: if idempotency_token is not None:
self._set_idempotency_token_arn(idempotency_token, cert.arn) self._set_idempotency_token_arn(idempotency_token, cert.arn)

View File

@ -6,6 +6,9 @@ from .models import acm_backends, AWSValidationException
class AWSCertificateManagerResponse(BaseResponse): class AWSCertificateManagerResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="acm")
@property @property
def acm_backend(self): def acm_backend(self):
""" """
@ -14,7 +17,7 @@ class AWSCertificateManagerResponse(BaseResponse):
:return: ACM Backend object :return: ACM Backend object
:rtype: moto.acm.models.AWSCertificateManagerBackend :rtype: moto.acm.models.AWSCertificateManagerBackend
""" """
return acm_backends[self.region] return acm_backends[self.current_account][self.region]
@property @property
def request_params(self): def request_params(self):

View File

@ -13,7 +13,7 @@ from urllib.parse import urlparse
import responses import responses
from openapi_spec_validator.exceptions import OpenAPIValidationError from openapi_spec_validator.exceptions import OpenAPIValidationError
from moto.core import get_account_id, BaseBackend, BaseModel, CloudFormationModel from moto.core import BaseBackend, BaseModel, CloudFormationModel
from .utils import create_id, to_path from .utils import create_id, to_path
from moto.core.utils import path_url, BackendDict from moto.core.utils import path_url, BackendDict
from .integration_parsers.aws_parser import TypeAwsParser from .integration_parsers.aws_parser import TypeAwsParser
@ -84,13 +84,13 @@ class Deployment(CloudFormationModel, dict):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
rest_api_id = properties["RestApiId"] rest_api_id = properties["RestApiId"]
name = properties["StageName"] name = properties["StageName"]
desc = properties.get("Description", "") desc = properties.get("Description", "")
backend = apigateway_backends[region_name] backend = apigateway_backends[account_id][region_name]
return backend.create_deployment( return backend.create_deployment(
function_id=rest_api_id, name=name, description=desc function_id=rest_api_id, name=name, description=desc
) )
@ -209,7 +209,7 @@ class Method(CloudFormationModel, dict):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
rest_api_id = properties["RestApiId"] rest_api_id = properties["RestApiId"]
@ -217,7 +217,7 @@ class Method(CloudFormationModel, dict):
method_type = properties["HttpMethod"] method_type = properties["HttpMethod"]
auth_type = properties["AuthorizationType"] auth_type = properties["AuthorizationType"]
key_req = properties["ApiKeyRequired"] key_req = properties["ApiKeyRequired"]
backend = apigateway_backends[region_name] backend = apigateway_backends[account_id][region_name]
m = backend.put_method( m = backend.put_method(
function_id=rest_api_id, function_id=rest_api_id,
resource_id=resource_id, resource_id=resource_id,
@ -253,9 +253,12 @@ class Method(CloudFormationModel, dict):
class Resource(CloudFormationModel): class Resource(CloudFormationModel):
def __init__(self, resource_id, region_name, api_id, path_part, parent_id): def __init__(
self, resource_id, account_id, region_name, api_id, path_part, parent_id
):
super().__init__() super().__init__()
self.id = resource_id self.id = resource_id
self.account_id = account_id
self.region_name = region_name self.region_name = region_name
self.api_id = api_id self.api_id = api_id
self.path_part = path_part self.path_part = path_part
@ -291,14 +294,14 @@ class Resource(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
api_id = properties["RestApiId"] api_id = properties["RestApiId"]
parent = properties["ParentId"] parent = properties["ParentId"]
path = properties["PathPart"] path = properties["PathPart"]
backend = apigateway_backends[region_name] backend = apigateway_backends[account_id][region_name]
if parent == api_id: if parent == api_id:
# A Root path (/) is automatically created. Any new paths should use this as their parent # A Root path (/) is automatically created. Any new paths should use this as their parent
resources = backend.get_resources(function_id=api_id) resources = backend.get_resources(function_id=api_id)
@ -315,7 +318,7 @@ class Resource(CloudFormationModel):
def get_parent_path(self): def get_parent_path(self):
if self.parent_id: if self.parent_id:
backend = apigateway_backends[self.region_name] backend = apigateway_backends[self.account_id][self.region_name]
parent = backend.get_resource(self.api_id, self.parent_id) parent = backend.get_resource(self.api_id, self.parent_id)
parent_path = parent.get_path() parent_path = parent.get_path()
if parent_path != "/": # Root parent if parent_path != "/": # Root parent
@ -780,9 +783,10 @@ class RestAPI(CloudFormationModel):
OPERATION_VALUE = "value" OPERATION_VALUE = "value"
OPERATION_OP = "op" OPERATION_OP = "op"
def __init__(self, api_id, region_name, name, description, **kwargs): def __init__(self, api_id, account_id, region_name, name, description, **kwargs):
super().__init__() super().__init__()
self.id = api_id self.id = api_id
self.account_id = account_id
self.region_name = region_name self.region_name = region_name
self.name = name self.name = name
self.description = description self.description = description
@ -883,13 +887,13 @@ class RestAPI(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
name = properties["Name"] name = properties["Name"]
desc = properties.get("Description", "") desc = properties.get("Description", "")
config = properties.get("EndpointConfiguration", None) config = properties.get("EndpointConfiguration", None)
backend = apigateway_backends[region_name] backend = apigateway_backends[account_id][region_name]
return backend.create_rest_api( return backend.create_rest_api(
name=name, description=desc, endpoint_configuration=config name=name, description=desc, endpoint_configuration=config
) )
@ -898,6 +902,7 @@ class RestAPI(CloudFormationModel):
child_id = create_id() child_id = create_id()
child = Resource( child = Resource(
resource_id=child_id, resource_id=child_id,
account_id=self.account_id,
region_name=self.region_name, region_name=self.region_name,
api_id=self.id, api_id=self.id,
path_part=path, path_part=path,
@ -1267,6 +1272,7 @@ class APIGatewayBackend(BaseBackend):
api_id = create_id() api_id = create_id()
rest_api = RestAPI( rest_api = RestAPI(
api_id, api_id,
self.account_id,
self.region_name, self.region_name,
name, name,
description, description,
@ -1576,7 +1582,7 @@ class APIGatewayBackend(BaseBackend):
): ):
resource = self.get_resource(function_id, resource_id) resource = self.get_resource(function_id, resource_id)
if credentials and not re.match( if credentials and not re.match(
"^arn:aws:iam::" + str(get_account_id()), credentials "^arn:aws:iam::" + str(self.account_id), credentials
): ):
raise CrossAccountNotAllowed() raise CrossAccountNotAllowed()
if not integration_method and integration_type in [ if not integration_method and integration_type in [

View File

@ -13,6 +13,9 @@ ENDPOINT_CONFIGURATION_TYPES = ["PRIVATE", "EDGE", "REGIONAL"]
class APIGatewayResponse(BaseResponse): class APIGatewayResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="apigateway")
def error(self, type_, message, status=400): def error(self, type_, message, status=400):
headers = self.response_headers or {} headers = self.response_headers or {}
headers["X-Amzn-Errortype"] = type_ headers["X-Amzn-Errortype"] = type_
@ -20,7 +23,7 @@ class APIGatewayResponse(BaseResponse):
@property @property
def backend(self): def backend(self):
return apigateway_backends[self.region] return apigateway_backends[self.current_account][self.region]
def __validate_api_key_source(self, api_key_source): def __validate_api_key_source(self, api_key_source):
if api_key_source and api_key_source not in API_KEY_SOURCES: if api_key_source and api_key_source not in API_KEY_SOURCES:

View File

@ -11,10 +11,13 @@ from .models import apigatewayv2_backends
class ApiGatewayV2Response(BaseResponse): class ApiGatewayV2Response(BaseResponse):
"""Handler for ApiGatewayV2 requests and responses.""" """Handler for ApiGatewayV2 requests and responses."""
def __init__(self):
super().__init__(service_name="apigatewayv2")
@property @property
def apigatewayv2_backend(self): def apigatewayv2_backend(self):
"""Return backend instance specific for this region.""" """Return backend instance specific for this region."""
return apigatewayv2_backends[self.region] return apigatewayv2_backends[self.current_account][self.region]
def apis(self, request, full_url, headers): def apis(self, request, full_url, headers):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)

View File

@ -1,4 +1,4 @@
from moto.core import get_account_id, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict from moto.core.utils import BackendDict
from moto.ecs import ecs_backends from moto.ecs import ecs_backends
from .exceptions import AWSValidationException from .exceptions import AWSValidationException
@ -65,7 +65,7 @@ class ScalableDimensionValueSet(Enum):
class ApplicationAutoscalingBackend(BaseBackend): class ApplicationAutoscalingBackend(BaseBackend):
def __init__(self, region_name, account_id): def __init__(self, region_name, account_id):
super().__init__(region_name, account_id) super().__init__(region_name, account_id)
self.ecs_backend = ecs_backends[region_name] self.ecs_backend = ecs_backends[account_id][region_name]
self.targets = OrderedDict() self.targets = OrderedDict()
self.policies = {} self.policies = {}
self.scheduled_actions = list() self.scheduled_actions = list()
@ -77,10 +77,6 @@ class ApplicationAutoscalingBackend(BaseBackend):
service_region, zones, "application-autoscaling" service_region, zones, "application-autoscaling"
) )
@property
def applicationautoscaling_backend(self):
return applicationautoscaling_backends[self.region_name]
def describe_scalable_targets(self, namespace, r_ids=None, dimension=None): def describe_scalable_targets(self, namespace, r_ids=None, dimension=None):
"""Describe scalable targets.""" """Describe scalable targets."""
if r_ids is None: if r_ids is None:
@ -305,6 +301,7 @@ class ApplicationAutoscalingBackend(BaseBackend):
start_time, start_time,
end_time, end_time,
scalable_target_action, scalable_target_action,
self.account_id,
self.region_name, self.region_name,
) )
self.scheduled_actions.append(action) self.scheduled_actions.append(action)
@ -450,9 +447,10 @@ class FakeScheduledAction(BaseModel):
start_time, start_time,
end_time, end_time,
scalable_target_action, scalable_target_action,
account_id,
region, region,
): ):
self.arn = f"arn:aws:autoscaling:{region}:{get_account_id()}:scheduledAction:{service_namespace}:scheduledActionName/{scheduled_action_name}" self.arn = f"arn:aws:autoscaling:{region}:{account_id}:scheduledAction:{service_namespace}:scheduledActionName/{scheduled_action_name}"
self.service_namespace = service_namespace self.service_namespace = service_namespace
self.schedule = schedule self.schedule = schedule
self.timezone = timezone self.timezone = timezone

View File

@ -9,9 +9,12 @@ from .exceptions import AWSValidationException
class ApplicationAutoScalingResponse(BaseResponse): class ApplicationAutoScalingResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="application-autoscaling")
@property @property
def applicationautoscaling_backend(self): def applicationautoscaling_backend(self):
return applicationautoscaling_backends[self.region] return applicationautoscaling_backends[self.current_account][self.region]
def describe_scalable_targets(self): def describe_scalable_targets(self):
self._validate_params() self._validate_params()

View File

@ -1,6 +1,6 @@
import base64 import base64
from datetime import timedelta, datetime, timezone from datetime import timedelta, datetime, timezone
from moto.core import get_account_id, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict, unix_time from moto.core.utils import BackendDict, unix_time
from moto.utilities.tagging_service import TaggingService from moto.utilities.tagging_service import TaggingService
@ -53,6 +53,7 @@ class GraphqlSchema(BaseModel):
class GraphqlAPI(BaseModel): class GraphqlAPI(BaseModel):
def __init__( def __init__(
self, self,
account_id,
region, region,
name, name,
authentication_type, authentication_type,
@ -74,9 +75,7 @@ class GraphqlAPI(BaseModel):
self.user_pool_config = user_pool_config self.user_pool_config = user_pool_config
self.xray_enabled = xray_enabled self.xray_enabled = xray_enabled
self.arn = ( self.arn = f"arn:aws:appsync:{self.region}:{account_id}:apis/{self.api_id}"
f"arn:aws:appsync:{self.region}:{get_account_id()}:apis/{self.api_id}"
)
self.graphql_schema = None self.graphql_schema = None
self.api_keys = dict() self.api_keys = dict()
@ -205,6 +204,7 @@ class AppSyncBackend(BaseBackend):
tags, tags,
): ):
graphql_api = GraphqlAPI( graphql_api = GraphqlAPI(
account_id=self.account_id,
region=self.region_name, region=self.region_name,
name=name, name=name,
authentication_type=authentication_type, authentication_type=authentication_type,

View File

@ -9,10 +9,13 @@ from .models import appsync_backends
class AppSyncResponse(BaseResponse): class AppSyncResponse(BaseResponse):
"""Handler for AppSync requests and responses.""" """Handler for AppSync requests and responses."""
def __init__(self):
super().__init__(service_name="appsync")
@property @property
def appsync_backend(self): def appsync_backend(self):
"""Return backend instance specific for this region.""" """Return backend instance specific for this region."""
return appsync_backends[self.region] return appsync_backends[self.current_account][self.region]
def graph_ql(self, request, full_url, headers): def graph_ql(self, request, full_url, headers):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)
@ -114,7 +117,6 @@ class AppSyncResponse(BaseResponse):
log_config = params.get("logConfig") log_config = params.get("logConfig")
authentication_type = params.get("authenticationType") authentication_type = params.get("authenticationType")
user_pool_config = params.get("userPoolConfig") user_pool_config = params.get("userPoolConfig")
print(user_pool_config)
open_id_connect_config = params.get("openIDConnectConfig") open_id_connect_config = params.get("openIDConnectConfig")
additional_authentication_providers = params.get( additional_authentication_providers = params.get(
"additionalAuthenticationProviders" "additionalAuthenticationProviders"
@ -152,7 +154,6 @@ class AppSyncResponse(BaseResponse):
api_key = self.appsync_backend.create_api_key( api_key = self.appsync_backend.create_api_key(
api_id=api_id, description=description, expires=expires api_id=api_id, description=description, expires=expires
) )
print(api_key.to_json())
return 200, {}, json.dumps(dict(apiKey=api_key.to_json())) return 200, {}, json.dumps(dict(apiKey=api_key.to_json()))
def delete_api_key(self): def delete_api_key(self):

View File

@ -1,6 +1,6 @@
import time import time
from moto.core import BaseBackend, BaseModel, get_account_id from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict from moto.core.utils import BackendDict
from uuid import uuid4 from uuid import uuid4
@ -10,18 +10,11 @@ class TaggableResourceMixin(object):
# This mixing was copied from Redshift when initially implementing # This mixing was copied from Redshift when initially implementing
# Athena. TBD if it's worth the overhead. # Athena. TBD if it's worth the overhead.
def __init__(self, region_name, resource_name, tags): def __init__(self, account_id, region_name, resource_name, tags):
self.region = region_name self.region = region_name
self.resource_name = resource_name self.resource_name = resource_name
self.tags = tags or [] self.tags = tags or []
self.arn = f"arn:aws:athena:{region_name}:{account_id}:{resource_name}"
@property
def arn(self):
return "arn:aws:athena:{region}:{account_id}:{resource_name}".format(
region=self.region,
account_id=get_account_id(),
resource_name=self.resource_name,
)
def create_tags(self, tags): def create_tags(self, tags):
new_keys = [tag_set["Key"] for tag_set in tags] new_keys = [tag_set["Key"] for tag_set in tags]
@ -41,7 +34,12 @@ class WorkGroup(TaggableResourceMixin, BaseModel):
def __init__(self, athena_backend, name, configuration, description, tags): def __init__(self, athena_backend, name, configuration, description, tags):
self.region_name = athena_backend.region_name self.region_name = athena_backend.region_name
super().__init__(self.region_name, "workgroup/{}".format(name), tags) super().__init__(
athena_backend.account_id,
self.region_name,
"workgroup/{}".format(name),
tags,
)
self.athena_backend = athena_backend self.athena_backend = athena_backend
self.name = name self.name = name
self.description = description self.description = description
@ -53,7 +51,12 @@ class DataCatalog(TaggableResourceMixin, BaseModel):
self, athena_backend, name, catalog_type, description, parameters, tags self, athena_backend, name, catalog_type, description, parameters, tags
): ):
self.region_name = athena_backend.region_name self.region_name = athena_backend.region_name
super().__init__(self.region_name, "datacatalog/{}".format(name), tags) super().__init__(
athena_backend.account_id,
self.region_name,
"datacatalog/{}".format(name),
tags,
)
self.athena_backend = athena_backend self.athena_backend = athena_backend
self.name = name self.name = name
self.type = catalog_type self.type = catalog_type

View File

@ -5,9 +5,12 @@ from .models import athena_backends
class AthenaResponse(BaseResponse): class AthenaResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="athena")
@property @property
def athena_backend(self): def athena_backend(self):
return athena_backends[self.region] return athena_backends[self.current_account][self.region]
def create_work_group(self): def create_work_group(self):
name = self._get_param("Name") name = self._get_param("Name")

View File

@ -9,7 +9,7 @@ from moto.packages.boto.ec2.blockdevicemapping import (
from moto.ec2.exceptions import InvalidInstanceIdError from moto.ec2.exceptions import InvalidInstanceIdError
from collections import OrderedDict from collections import OrderedDict
from moto.core import get_account_id, BaseBackend, BaseModel, CloudFormationModel from moto.core import BaseBackend, BaseModel, CloudFormationModel
from moto.core.utils import camelcase_to_underscores, BackendDict from moto.core.utils import camelcase_to_underscores, BackendDict
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from moto.elb import elb_backends from moto.elb import elb_backends
@ -97,7 +97,7 @@ class FakeScalingPolicy(BaseModel):
@property @property
def arn(self): def arn(self):
return f"arn:aws:autoscaling:{self.autoscaling_backend.region_name}:{get_account_id()}:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/{self.as_name}:policyName/{self.name}" return f"arn:aws:autoscaling:{self.autoscaling_backend.region_name}:{self.autoscaling_backend.account_id}:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/{self.as_name}:policyName/{self.name}"
def execute(self): def execute(self):
if self.adjustment_type == "ExactCapacity": if self.adjustment_type == "ExactCapacity":
@ -131,6 +131,7 @@ class FakeLaunchConfiguration(CloudFormationModel):
ebs_optimized, ebs_optimized,
associate_public_ip_address, associate_public_ip_address,
block_device_mapping_dict, block_device_mapping_dict,
account_id,
region_name, region_name,
metadata_options, metadata_options,
classic_link_vpc_id, classic_link_vpc_id,
@ -157,7 +158,7 @@ class FakeLaunchConfiguration(CloudFormationModel):
self.metadata_options = metadata_options self.metadata_options = metadata_options
self.classic_link_vpc_id = classic_link_vpc_id self.classic_link_vpc_id = classic_link_vpc_id
self.classic_link_vpc_security_groups = classic_link_vpc_security_groups self.classic_link_vpc_security_groups = classic_link_vpc_security_groups
self.arn = f"arn:aws:autoscaling:{region_name}:{get_account_id()}:launchConfiguration:9dbbbf87-6141-428a-a409-0752edbe6cad:launchConfigurationName/{self.name}" self.arn = f"arn:aws:autoscaling:{region_name}:{account_id}:launchConfiguration:9dbbbf87-6141-428a-a409-0752edbe6cad:launchConfigurationName/{self.name}"
@classmethod @classmethod
def create_from_instance(cls, name, instance, backend): def create_from_instance(cls, name, instance, backend):
@ -191,13 +192,13 @@ class FakeLaunchConfiguration(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
instance_profile_name = properties.get("IamInstanceProfile") instance_profile_name = properties.get("IamInstanceProfile")
backend = autoscaling_backends[region_name] backend = autoscaling_backends[account_id][region_name]
config = backend.create_launch_configuration( config = backend.create_launch_configuration(
name=resource_name, name=resource_name,
image_id=properties.get("ImageId"), image_id=properties.get("ImageId"),
@ -218,27 +219,32 @@ class FakeLaunchConfiguration(CloudFormationModel):
@classmethod @classmethod
def update_from_cloudformation_json( def update_from_cloudformation_json(
cls, original_resource, new_resource_name, cloudformation_json, region_name cls,
original_resource,
new_resource_name,
cloudformation_json,
account_id,
region_name,
): ):
cls.delete_from_cloudformation_json( cls.delete_from_cloudformation_json(
original_resource.name, cloudformation_json, region_name original_resource.name, cloudformation_json, account_id, region_name
) )
return cls.create_from_cloudformation_json( return cls.create_from_cloudformation_json(
new_resource_name, cloudformation_json, region_name new_resource_name, cloudformation_json, account_id, region_name
) )
@classmethod @classmethod
def delete_from_cloudformation_json( def delete_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name cls, resource_name, cloudformation_json, account_id, region_name
): ):
backend = autoscaling_backends[region_name] backend = autoscaling_backends[account_id][region_name]
try: try:
backend.delete_launch_configuration(resource_name) backend.delete_launch_configuration(resource_name)
except KeyError: except KeyError:
pass pass
def delete(self, region_name): def delete(self, account_id, region_name):
backend = autoscaling_backends[region_name] backend = autoscaling_backends[account_id][region_name]
backend.delete_launch_configuration(self.name) backend.delete_launch_configuration(self.name)
@property @property
@ -315,12 +321,12 @@ class FakeScheduledAction(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
backend = autoscaling_backends[region_name] backend = autoscaling_backends[account_id][region_name]
scheduled_action_name = ( scheduled_action_name = (
kwargs["LogicalId"] kwargs["LogicalId"]
@ -369,6 +375,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
self.name = name self.name = name
self._id = str(uuid4()) self._id = str(uuid4())
self.region = self.autoscaling_backend.region_name self.region = self.autoscaling_backend.region_name
self.account_id = self.autoscaling_backend.account_id
self._set_azs_and_vpcs(availability_zones, vpc_zone_identifier) self._set_azs_and_vpcs(availability_zones, vpc_zone_identifier)
@ -415,7 +422,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
@property @property
def arn(self): def arn(self):
return f"arn:aws:autoscaling:{self.region}:{get_account_id()}:autoScalingGroup:{self._id}:autoScalingGroupName/{self.name}" return f"arn:aws:autoscaling:{self.region}:{self.account_id}:autoScalingGroup:{self._id}:autoScalingGroupName/{self.name}"
def active_instances(self): def active_instances(self):
return [x for x in self.instance_states if x.lifecycle_state == "InService"] return [x for x in self.instance_states if x.lifecycle_state == "InService"]
@ -498,7 +505,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
@ -510,7 +517,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
load_balancer_names = properties.get("LoadBalancerNames", []) load_balancer_names = properties.get("LoadBalancerNames", [])
target_group_arns = properties.get("TargetGroupARNs", []) target_group_arns = properties.get("TargetGroupARNs", [])
backend = autoscaling_backends[region_name] backend = autoscaling_backends[account_id][region_name]
group = backend.create_auto_scaling_group( group = backend.create_auto_scaling_group(
name=resource_name, name=resource_name,
availability_zones=properties.get("AvailabilityZones", []), availability_zones=properties.get("AvailabilityZones", []),
@ -540,27 +547,32 @@ class FakeAutoScalingGroup(CloudFormationModel):
@classmethod @classmethod
def update_from_cloudformation_json( def update_from_cloudformation_json(
cls, original_resource, new_resource_name, cloudformation_json, region_name cls,
original_resource,
new_resource_name,
cloudformation_json,
account_id,
region_name,
): ):
cls.delete_from_cloudformation_json( cls.delete_from_cloudformation_json(
original_resource.name, cloudformation_json, region_name original_resource.name, cloudformation_json, account_id, region_name
) )
return cls.create_from_cloudformation_json( return cls.create_from_cloudformation_json(
new_resource_name, cloudformation_json, region_name new_resource_name, cloudformation_json, account_id, region_name
) )
@classmethod @classmethod
def delete_from_cloudformation_json( def delete_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name cls, resource_name, cloudformation_json, account_id, region_name
): ):
backend = autoscaling_backends[region_name] backend = autoscaling_backends[account_id][region_name]
try: try:
backend.delete_auto_scaling_group(resource_name) backend.delete_auto_scaling_group(resource_name)
except KeyError: except KeyError:
pass pass
def delete(self, region_name): def delete(self, account_id, region_name):
backend = autoscaling_backends[region_name] backend = autoscaling_backends[account_id][region_name]
backend.delete_auto_scaling_group(self.name) backend.delete_auto_scaling_group(self.name)
@property @property
@ -740,9 +752,9 @@ class AutoScalingBackend(BaseBackend):
self.scheduled_actions = OrderedDict() self.scheduled_actions = OrderedDict()
self.policies = {} self.policies = {}
self.lifecycle_hooks = {} self.lifecycle_hooks = {}
self.ec2_backend = ec2_backends[region_name] self.ec2_backend = ec2_backends[self.account_id][region_name]
self.elb_backend = elb_backends[region_name] self.elb_backend = elb_backends[self.account_id][region_name]
self.elbv2_backend = elbv2_backends[region_name] self.elbv2_backend = elbv2_backends[self.account_id][region_name]
@staticmethod @staticmethod
def default_vpc_endpoint_service(service_region, zones): def default_vpc_endpoint_service(service_region, zones):
@ -800,6 +812,7 @@ class AutoScalingBackend(BaseBackend):
ebs_optimized=ebs_optimized, ebs_optimized=ebs_optimized,
associate_public_ip_address=associate_public_ip_address, associate_public_ip_address=associate_public_ip_address,
block_device_mapping_dict=block_device_mappings, block_device_mapping_dict=block_device_mappings,
account_id=self.account_id,
region_name=self.region_name, region_name=self.region_name,
metadata_options=metadata_options, metadata_options=metadata_options,
classic_link_vpc_id=classic_link_vpc_id, classic_link_vpc_id=classic_link_vpc_id,

View File

@ -10,9 +10,12 @@ from .models import autoscaling_backends
class AutoScalingResponse(BaseResponse): class AutoScalingResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="autoscaling")
@property @property
def autoscaling_backend(self): def autoscaling_backend(self):
return autoscaling_backends[self.region] return autoscaling_backends[self.current_account][self.region]
def create_launch_configuration(self): def create_launch_configuration(self):
instance_monitoring_string = self._get_param("InstanceMonitoring.Enabled") instance_monitoring_string = self._get_param("InstanceMonitoring.Enabled")

View File

@ -51,7 +51,6 @@ from .utils import (
from moto.sqs import sqs_backends from moto.sqs import sqs_backends
from moto.dynamodb import dynamodb_backends from moto.dynamodb import dynamodb_backends
from moto.dynamodbstreams import dynamodbstreams_backends from moto.dynamodbstreams import dynamodbstreams_backends
from moto.core import get_account_id
from moto.utilities.docker_utilities import DockerModel, parse_image_ref from moto.utilities.docker_utilities import DockerModel, parse_image_ref
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from uuid import uuid4 from uuid import uuid4
@ -179,11 +178,13 @@ def _s3_content(key):
return key.value, key.size, base64ed_sha, sha_hex_digest return key.value, key.size, base64ed_sha, sha_hex_digest
def _validate_s3_bucket_and_key(data): def _validate_s3_bucket_and_key(account_id, data):
key = None key = None
try: try:
# FIXME: does not validate bucket region # FIXME: does not validate bucket region
key = s3_backends["global"].get_object(data["S3Bucket"], data["S3Key"]) key = s3_backends[account_id]["global"].get_object(
data["S3Bucket"], data["S3Key"]
)
except MissingBucket: except MissingBucket:
if do_validate_s3(): if do_validate_s3():
raise InvalidParameterValueException( raise InvalidParameterValueException(
@ -212,18 +213,19 @@ class Permission(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
backend = lambda_backends[region_name] backend = lambda_backends[account_id][region_name]
fn = backend.get_function(properties["FunctionName"]) fn = backend.get_function(properties["FunctionName"])
fn.policy.add_statement(raw=json.dumps(properties)) fn.policy.add_statement(raw=json.dumps(properties))
return Permission(region=region_name) return Permission(region=region_name)
class LayerVersion(CloudFormationModel): class LayerVersion(CloudFormationModel):
def __init__(self, spec, region): def __init__(self, spec, account_id, region):
# required # required
self.account_id = account_id
self.region = region self.region = region
self.name = spec["LayerName"] self.name = spec["LayerName"]
self.content = spec["Content"] self.content = spec["Content"]
@ -248,7 +250,7 @@ class LayerVersion(CloudFormationModel):
self.code_digest, self.code_digest,
) = _zipfile_content(self.content["ZipFile"]) ) = _zipfile_content(self.content["ZipFile"])
else: else:
key = _validate_s3_bucket_and_key(self.content) key = _validate_s3_bucket_and_key(account_id, data=self.content)
if key: if key:
( (
self.code_bytes, self.code_bytes,
@ -261,7 +263,7 @@ class LayerVersion(CloudFormationModel):
def arn(self): def arn(self):
if self.version: if self.version:
return make_layer_ver_arn( return make_layer_ver_arn(
self.region, get_account_id(), self.name, self.version self.region, self.account_id, self.name, self.version
) )
raise ValueError("Layer version is not set") raise ValueError("Layer version is not set")
@ -297,7 +299,7 @@ class LayerVersion(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
optional_properties = ("Description", "CompatibleRuntimes", "LicenseInfo") optional_properties = ("Description", "CompatibleRuntimes", "LicenseInfo")
@ -311,16 +313,25 @@ class LayerVersion(CloudFormationModel):
if prop in properties: if prop in properties:
spec[prop] = properties[prop] spec[prop] = properties[prop]
backend = lambda_backends[region_name] backend = lambda_backends[account_id][region_name]
layer_version = backend.publish_layer_version(spec) layer_version = backend.publish_layer_version(spec)
return layer_version return layer_version
class LambdaAlias(BaseModel): class LambdaAlias(BaseModel):
def __init__( def __init__(
self, region, name, function_name, function_version, description, routing_config self,
account_id,
region,
name,
function_name,
function_version,
description,
routing_config,
): ):
self.arn = f"arn:aws:lambda:{region}:{get_account_id()}:function:{function_name}:{name}" self.arn = (
f"arn:aws:lambda:{region}:{account_id}:function:{function_name}:{name}"
)
self.name = name self.name = name
self.function_version = function_version self.function_version = function_version
self.description = description self.description = description
@ -347,11 +358,13 @@ class LambdaAlias(BaseModel):
class Layer(object): class Layer(object):
def __init__(self, name, region): def __init__(self, layer_version: LayerVersion):
self.region = region self.region = layer_version.region
self.name = name self.name = layer_version.name
self.layer_arn = make_layer_arn(region, get_account_id(), self.name) self.layer_arn = make_layer_arn(
self.region, layer_version.account_id, self.name
)
self._latest_version = 0 self._latest_version = 0
self.layer_versions = {} self.layer_versions = {}
@ -378,16 +391,17 @@ class Layer(object):
class LambdaFunction(CloudFormationModel, DockerModel): class LambdaFunction(CloudFormationModel, DockerModel):
def __init__(self, spec, region, version=1): def __init__(self, account_id, spec, region, version=1):
DockerModel.__init__(self) DockerModel.__init__(self)
# required # required
self.account_id = account_id
self.region = region self.region = region
self.code = spec["Code"] self.code = spec["Code"]
self.function_name = spec["FunctionName"] self.function_name = spec["FunctionName"]
self.handler = spec.get("Handler") self.handler = spec.get("Handler")
self.role = spec["Role"] self.role = spec["Role"]
self.run_time = spec.get("Runtime") self.run_time = spec.get("Runtime")
self.logs_backend = logs_backends[self.region] self.logs_backend = logs_backends[account_id][self.region]
self.environment_vars = spec.get("Environment", {}).get("Variables", {}) self.environment_vars = spec.get("Environment", {}).get("Variables", {})
self.policy = None self.policy = None
self.state = "Active" self.state = "Active"
@ -428,7 +442,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
self.code["UUID"] = str(uuid.uuid4()) self.code["UUID"] = str(uuid.uuid4())
self.code["S3Key"] = "{}-{}".format(self.function_name, self.code["UUID"]) self.code["S3Key"] = "{}-{}".format(self.function_name, self.code["UUID"])
elif "S3Bucket" in self.code: elif "S3Bucket" in self.code:
key = _validate_s3_bucket_and_key(self.code) key = _validate_s3_bucket_and_key(self.account_id, data=self.code)
if key: if key:
( (
self.code_bytes, self.code_bytes,
@ -447,7 +461,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
self.code_size = 0 self.code_size = 0
self.function_arn = make_function_arn( self.function_arn = make_function_arn(
self.region, get_account_id(), self.function_name self.region, self.account_id, self.function_name
) )
if spec.get("Tags"): if spec.get("Tags"):
@ -459,7 +473,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
def set_version(self, version): def set_version(self, version):
self.function_arn = make_function_ver_arn( self.function_arn = make_function_ver_arn(
self.region, get_account_id(), self.function_name, version self.region, self.account_id, self.function_name, version
) )
self.version = version self.version = version
self.last_modified = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") self.last_modified = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
@ -479,7 +493,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
return json.dumps(self.get_configuration()) return json.dumps(self.get_configuration())
def _get_layers_data(self, layers_versions_arns): def _get_layers_data(self, layers_versions_arns):
backend = lambda_backends[self.region] backend = lambda_backends[self.account_id][self.region]
layer_versions = [ layer_versions = [
backend.layers_versions_by_arn(layer_version) backend.layers_versions_by_arn(layer_version)
for layer_version in layers_versions_arns for layer_version in layers_versions_arns
@ -602,7 +616,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
key = None key = None
try: try:
# FIXME: does not validate bucket region # FIXME: does not validate bucket region
key = s3_backends["global"].get_object( key = s3_backends[self.account_id]["global"].get_object(
updated_spec["S3Bucket"], updated_spec["S3Key"] updated_spec["S3Bucket"], updated_spec["S3Key"]
) )
except MissingBucket: except MissingBucket:
@ -791,7 +805,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
optional_properties = ( optional_properties = (
@ -827,7 +841,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
cls._create_zipfile_from_plaintext_code(spec["Code"]["ZipFile"]) cls._create_zipfile_from_plaintext_code(spec["Code"]["ZipFile"])
) )
backend = lambda_backends[region_name] backend = lambda_backends[account_id][region_name]
fn = backend.create_function(spec) fn = backend.create_function(spec)
return fn return fn
@ -839,12 +853,17 @@ class LambdaFunction(CloudFormationModel, DockerModel):
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
if attribute_name == "Arn": if attribute_name == "Arn":
return make_function_arn(self.region, get_account_id(), self.function_name) return make_function_arn(self.region, self.account_id, self.function_name)
raise UnformattedGetAttTemplateException() raise UnformattedGetAttTemplateException()
@classmethod @classmethod
def update_from_cloudformation_json( def update_from_cloudformation_json(
cls, original_resource, new_resource_name, cloudformation_json, region_name cls,
original_resource,
new_resource_name,
cloudformation_json,
account_id,
region_name,
): ):
updated_props = cloudformation_json["Properties"] updated_props = cloudformation_json["Properties"]
original_resource.update_configuration(updated_props) original_resource.update_configuration(updated_props)
@ -865,8 +884,8 @@ class LambdaFunction(CloudFormationModel, DockerModel):
zip_output.seek(0) zip_output.seek(0)
return zip_output.read() return zip_output.read()
def delete(self, region): def delete(self, account_id, region):
lambda_backends[region].delete_function(self.function_name) lambda_backends[account_id][region].delete_function(self.function_name)
def delete_alias(self, name): def delete_alias(self, name):
self._aliases.pop(name, None) self._aliases.pop(name, None)
@ -874,11 +893,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
def get_alias(self, name): def get_alias(self, name):
if name in self._aliases: if name in self._aliases:
return self._aliases[name] return self._aliases[name]
arn = f"arn:aws:lambda:{self.region}:{get_account_id()}:function:{self.function_name}:{name}" arn = f"arn:aws:lambda:{self.region}:{self.account_id}:function:{self.function_name}:{name}"
raise UnknownAliasException(arn) raise UnknownAliasException(arn)
def put_alias(self, name, description, function_version, routing_config): def put_alias(self, name, description, function_version, routing_config):
alias = LambdaAlias( alias = LambdaAlias(
account_id=self.account_id,
region=self.region, region=self.region,
name=name, name=name,
function_name=self.function_name, function_name=self.function_name,
@ -968,8 +988,8 @@ class EventSourceMapping(CloudFormationModel):
"StateTransitionReason": "User initiated", "StateTransitionReason": "User initiated",
} }
def delete(self, region_name): def delete(self, account_id, region_name):
lambda_backend = lambda_backends[region_name] lambda_backend = lambda_backends[account_id][region_name]
lambda_backend.delete_event_source_mapping(self.uuid) lambda_backend.delete_event_source_mapping(self.uuid)
@staticmethod @staticmethod
@ -983,27 +1003,32 @@ class EventSourceMapping(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
lambda_backend = lambda_backends[region_name] lambda_backend = lambda_backends[account_id][region_name]
return lambda_backend.create_event_source_mapping(properties) return lambda_backend.create_event_source_mapping(properties)
@classmethod @classmethod
def update_from_cloudformation_json( def update_from_cloudformation_json(
cls, original_resource, new_resource_name, cloudformation_json, region_name cls,
original_resource,
new_resource_name,
cloudformation_json,
account_id,
region_name,
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
event_source_uuid = original_resource.uuid event_source_uuid = original_resource.uuid
lambda_backend = lambda_backends[region_name] lambda_backend = lambda_backends[account_id][region_name]
return lambda_backend.update_event_source_mapping(event_source_uuid, properties) return lambda_backend.update_event_source_mapping(event_source_uuid, properties)
@classmethod @classmethod
def delete_from_cloudformation_json( def delete_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name cls, resource_name, cloudformation_json, account_id, region_name
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
lambda_backend = lambda_backends[region_name] lambda_backend = lambda_backends[account_id][region_name]
esms = lambda_backend.list_event_source_mappings( esms = lambda_backend.list_event_source_mappings(
event_source_arn=properties["EventSourceArn"], event_source_arn=properties["EventSourceArn"],
function_name=properties["FunctionName"], function_name=properties["FunctionName"],
@ -1011,7 +1036,7 @@ class EventSourceMapping(CloudFormationModel):
for esm in esms: for esm in esms:
if esm.uuid == resource_name: if esm.uuid == resource_name:
esm.delete(region_name) esm.delete(account_id, region_name)
@property @property
def physical_resource_id(self): def physical_resource_id(self):
@ -1036,22 +1061,23 @@ class LambdaVersion(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
function_name = properties["FunctionName"] function_name = properties["FunctionName"]
func = lambda_backends[region_name].publish_function(function_name) func = lambda_backends[account_id][region_name].publish_function(function_name)
spec = {"Version": func.version} spec = {"Version": func.version}
return LambdaVersion(spec) return LambdaVersion(spec)
class LambdaStorage(object): class LambdaStorage(object):
def __init__(self, region_name): def __init__(self, region_name, account_id):
# Format 'func_name' {'versions': []} # Format 'func_name' {'versions': []}
self._functions = {} self._functions = {}
self._aliases = dict() self._aliases = dict()
self._arns = weakref.WeakValueDictionary() self._arns = weakref.WeakValueDictionary()
self.region_name = region_name self.region_name = region_name
self.account_id = account_id
def _get_latest(self, name): def _get_latest(self, name):
return self._functions[name]["latest"] return self._functions[name]["latest"]
@ -1120,7 +1146,7 @@ class LambdaStorage(object):
if name_or_arn.startswith("arn:aws"): if name_or_arn.startswith("arn:aws"):
arn = name_or_arn arn = name_or_arn
else: else:
arn = make_function_arn(self.region_name, get_account_id(), name_or_arn) arn = make_function_arn(self.region_name, self.account_id, name_or_arn)
if qualifier: if qualifier:
arn = f"{arn}:{qualifier}" arn = f"{arn}:{qualifier}"
raise UnknownFunctionException(arn) raise UnknownFunctionException(arn)
@ -1134,10 +1160,11 @@ class LambdaStorage(object):
valid_role = re.match(InvalidRoleFormat.pattern, fn.role) valid_role = re.match(InvalidRoleFormat.pattern, fn.role)
if valid_role: if valid_role:
account = valid_role.group(2) account = valid_role.group(2)
if account != get_account_id(): if account != self.account_id:
raise CrossAccountNotAllowed() raise CrossAccountNotAllowed()
try: try:
iam_backends["global"].get_role_by_arn(fn.role) iam_backend = iam_backends[self.account_id]["global"]
iam_backend.get_role_by_arn(fn.role)
except IAMNotFoundException: except IAMNotFoundException:
raise InvalidParameterValueException( raise InvalidParameterValueException(
"The role defined for the function cannot be assumed by Lambda." "The role defined for the function cannot be assumed by Lambda."
@ -1240,9 +1267,7 @@ class LayerStorage(object):
:param layer_version: LayerVersion :param layer_version: LayerVersion
""" """
if layer_version.name not in self._layers: if layer_version.name not in self._layers:
self._layers[layer_version.name] = Layer( self._layers[layer_version.name] = Layer(layer_version)
layer_version.name, layer_version.region
)
self._layers[layer_version.name].attach_version(layer_version) self._layers[layer_version.name].attach_version(layer_version)
def list_layers(self): def list_layers(self):
@ -1328,7 +1353,7 @@ class LambdaBackend(BaseBackend):
def __init__(self, region_name, account_id): def __init__(self, region_name, account_id):
super().__init__(region_name, account_id) super().__init__(region_name, account_id)
self._lambdas = LambdaStorage(region_name=region_name) self._lambdas = LambdaStorage(region_name=region_name, account_id=account_id)
self._event_source_mappings = {} self._event_source_mappings = {}
self._layers = LayerStorage() self._layers = LayerStorage()
@ -1367,7 +1392,12 @@ class LambdaBackend(BaseBackend):
if function_name is None: if function_name is None:
raise RESTError("InvalidParameterValueException", "Missing FunctionName") raise RESTError("InvalidParameterValueException", "Missing FunctionName")
fn = LambdaFunction(spec, self.region_name, version="$LATEST") fn = LambdaFunction(
account_id=self.account_id,
spec=spec,
region=self.region_name,
version="$LATEST",
)
self._lambdas.put_function(fn) self._lambdas.put_function(fn)
@ -1393,7 +1423,8 @@ class LambdaBackend(BaseBackend):
raise RESTError("ResourceNotFoundException", "Invalid FunctionName") raise RESTError("ResourceNotFoundException", "Invalid FunctionName")
# Validate queue # Validate queue
for queue in sqs_backends[self.region_name].queues.values(): sqs_backend = sqs_backends[self.account_id][self.region_name]
for queue in sqs_backend.queues.values():
if queue.queue_arn == spec["EventSourceArn"]: if queue.queue_arn == spec["EventSourceArn"]:
if queue.lambda_event_source_mappings.get("func.function_arn"): if queue.lambda_event_source_mappings.get("func.function_arn"):
# TODO: Correct exception? # TODO: Correct exception?
@ -1414,15 +1445,15 @@ class LambdaBackend(BaseBackend):
queue.lambda_event_source_mappings[esm.function_arn] = esm queue.lambda_event_source_mappings[esm.function_arn] = esm
return esm return esm
for stream in json.loads( ddbstream_backend = dynamodbstreams_backends[self.account_id][self.region_name]
dynamodbstreams_backends[self.region_name].list_streams() ddb_backend = dynamodb_backends[self.account_id][self.region_name]
)["Streams"]: for stream in json.loads(ddbstream_backend.list_streams())["Streams"]:
if stream["StreamArn"] == spec["EventSourceArn"]: if stream["StreamArn"] == spec["EventSourceArn"]:
spec.update({"FunctionArn": func.function_arn}) spec.update({"FunctionArn": func.function_arn})
esm = EventSourceMapping(spec) esm = EventSourceMapping(spec)
self._event_source_mappings[esm.uuid] = esm self._event_source_mappings[esm.uuid] = esm
table_name = stream["TableName"] table_name = stream["TableName"]
table = dynamodb_backends[self.region_name].get_table(table_name) table = ddb_backend.get_table(table_name)
table.lambda_event_source_mappings[esm.function_arn] = esm table.lambda_event_source_mappings[esm.function_arn] = esm
return esm return esm
raise RESTError("ResourceNotFoundException", "Invalid EventSourceArn") raise RESTError("ResourceNotFoundException", "Invalid EventSourceArn")
@ -1432,7 +1463,9 @@ class LambdaBackend(BaseBackend):
for param in required: for param in required:
if not spec.get(param): if not spec.get(param):
raise InvalidParameterValueException("Missing {}".format(param)) raise InvalidParameterValueException("Missing {}".format(param))
layer_version = LayerVersion(spec, self.region_name) layer_version = LayerVersion(
spec, account_id=self.account_id, region=self.region_name
)
self._layers.put_layer_version(layer_version) self._layers.put_layer_version(layer_version)
return layer_version return layer_version
@ -1592,7 +1625,7 @@ class LambdaBackend(BaseBackend):
): ):
data = { data = {
"messageType": "DATA_MESSAGE", "messageType": "DATA_MESSAGE",
"owner": get_account_id(), "owner": self.account_id,
"logGroup": log_group_name, "logGroup": log_group_name,
"logStream": log_stream_name, "logStream": log_stream_name,
"subscriptionFilters": [filter_name], "subscriptionFilters": [filter_name],

View File

@ -9,6 +9,9 @@ from .models import lambda_backends
class LambdaResponse(BaseResponse): class LambdaResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="awslambda")
@property @property
def json_body(self): def json_body(self):
""" """
@ -18,13 +21,8 @@ class LambdaResponse(BaseResponse):
return json.loads(self.body) return json.loads(self.body)
@property @property
def lambda_backend(self): def backend(self):
""" return lambda_backends[self.current_account][self.region]
Get backend
:return: Lambda Backend
:rtype: moto.awslambda.models.LambdaBackend
"""
return lambda_backends[self.region]
def root(self, request, full_url, headers): def root(self, request, full_url, headers):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)
@ -195,13 +193,13 @@ class LambdaResponse(BaseResponse):
function_name = unquote(path.split("/")[-2]) function_name = unquote(path.split("/")[-2])
qualifier = self.querystring.get("Qualifier", [None])[0] qualifier = self.querystring.get("Qualifier", [None])[0]
statement = self.body statement = self.body
self.lambda_backend.add_permission(function_name, qualifier, statement) self.backend.add_permission(function_name, qualifier, statement)
return 200, {}, json.dumps({"Statement": statement}) return 200, {}, json.dumps({"Statement": statement})
def _get_policy(self, request): def _get_policy(self, request):
path = request.path if hasattr(request, "path") else path_url(request.url) path = request.path if hasattr(request, "path") else path_url(request.url)
function_name = unquote(path.split("/")[-2]) function_name = unquote(path.split("/")[-2])
out = self.lambda_backend.get_policy(function_name) out = self.backend.get_policy(function_name)
return 200, {}, out return 200, {}, out
def _del_policy(self, request, querystring): def _del_policy(self, request, querystring):
@ -209,8 +207,8 @@ class LambdaResponse(BaseResponse):
function_name = unquote(path.split("/")[-3]) function_name = unquote(path.split("/")[-3])
statement_id = path.split("/")[-1].split("?")[0] statement_id = path.split("/")[-1].split("?")[0]
revision = querystring.get("RevisionId", "") revision = querystring.get("RevisionId", "")
if self.lambda_backend.get_function(function_name): if self.backend.get_function(function_name):
self.lambda_backend.remove_permission(function_name, statement_id, revision) self.backend.remove_permission(function_name, statement_id, revision)
return 204, {}, "{}" return 204, {}, "{}"
else: else:
return 404, {}, "{}" return 404, {}, "{}"
@ -222,7 +220,7 @@ class LambdaResponse(BaseResponse):
function_name = unquote(self.path.rsplit("/", 2)[-2]) function_name = unquote(self.path.rsplit("/", 2)[-2])
qualifier = self._get_param("qualifier") qualifier = self._get_param("qualifier")
payload = self.lambda_backend.invoke( payload = self.backend.invoke(
function_name, qualifier, self.body, self.headers, response_headers function_name, qualifier, self.body, self.headers, response_headers
) )
if payload: if payload:
@ -254,7 +252,7 @@ class LambdaResponse(BaseResponse):
function_name = unquote(self.path.rsplit("/", 3)[-3]) function_name = unquote(self.path.rsplit("/", 3)[-3])
fn = self.lambda_backend.get_function(function_name, None) fn = self.backend.get_function(function_name, None)
payload = fn.invoke(self.body, self.headers, response_headers) payload = fn.invoke(self.body, self.headers, response_headers)
response_headers["Content-Length"] = str(len(payload)) response_headers["Content-Length"] = str(len(payload))
return 202, response_headers, payload return 202, response_headers, payload
@ -264,7 +262,7 @@ class LambdaResponse(BaseResponse):
func_version = querystring.get("FunctionVersion", [None])[0] func_version = querystring.get("FunctionVersion", [None])[0]
result = {"Functions": []} result = {"Functions": []}
for fn in self.lambda_backend.list_functions(func_version): for fn in self.backend.list_functions(func_version):
json_data = fn.get_configuration() json_data = fn.get_configuration()
result["Functions"].append(json_data) result["Functions"].append(json_data)
@ -273,7 +271,7 @@ class LambdaResponse(BaseResponse):
def _list_versions_by_function(self, function_name): def _list_versions_by_function(self, function_name):
result = {"Versions": []} result = {"Versions": []}
functions = self.lambda_backend.list_versions_by_function(function_name) functions = self.backend.list_versions_by_function(function_name)
if functions: if functions:
for fn in functions: for fn in functions:
json_data = fn.get_configuration() json_data = fn.get_configuration()
@ -282,38 +280,36 @@ class LambdaResponse(BaseResponse):
return 200, {}, json.dumps(result) return 200, {}, json.dumps(result)
def _create_function(self): def _create_function(self):
fn = self.lambda_backend.create_function(self.json_body) fn = self.backend.create_function(self.json_body)
config = fn.get_configuration(on_create=True) config = fn.get_configuration(on_create=True)
return 201, {}, json.dumps(config) return 201, {}, json.dumps(config)
def _create_event_source_mapping(self): def _create_event_source_mapping(self):
fn = self.lambda_backend.create_event_source_mapping(self.json_body) fn = self.backend.create_event_source_mapping(self.json_body)
config = fn.get_configuration() config = fn.get_configuration()
return 201, {}, json.dumps(config) return 201, {}, json.dumps(config)
def _list_event_source_mappings(self, event_source_arn, function_name): def _list_event_source_mappings(self, event_source_arn, function_name):
esms = self.lambda_backend.list_event_source_mappings( esms = self.backend.list_event_source_mappings(event_source_arn, function_name)
event_source_arn, function_name
)
result = {"EventSourceMappings": [esm.get_configuration() for esm in esms]} result = {"EventSourceMappings": [esm.get_configuration() for esm in esms]}
return 200, {}, json.dumps(result) return 200, {}, json.dumps(result)
def _get_event_source_mapping(self, uuid): def _get_event_source_mapping(self, uuid):
result = self.lambda_backend.get_event_source_mapping(uuid) result = self.backend.get_event_source_mapping(uuid)
if result: if result:
return 200, {}, json.dumps(result.get_configuration()) return 200, {}, json.dumps(result.get_configuration())
else: else:
return 404, {}, "{}" return 404, {}, "{}"
def _update_event_source_mapping(self, uuid): def _update_event_source_mapping(self, uuid):
result = self.lambda_backend.update_event_source_mapping(uuid, self.json_body) result = self.backend.update_event_source_mapping(uuid, self.json_body)
if result: if result:
return 202, {}, json.dumps(result.get_configuration()) return 202, {}, json.dumps(result.get_configuration())
else: else:
return 404, {}, "{}" return 404, {}, "{}"
def _delete_event_source_mapping(self, uuid): def _delete_event_source_mapping(self, uuid):
esm = self.lambda_backend.delete_event_source_mapping(uuid) esm = self.backend.delete_event_source_mapping(uuid)
if esm: if esm:
json_result = esm.get_configuration() json_result = esm.get_configuration()
json_result.update({"State": "Deleting"}) json_result.update({"State": "Deleting"})
@ -325,7 +321,7 @@ class LambdaResponse(BaseResponse):
function_name = unquote(self.path.split("/")[-2]) function_name = unquote(self.path.split("/")[-2])
description = self._get_param("Description") description = self._get_param("Description")
fn = self.lambda_backend.publish_function(function_name, description) fn = self.backend.publish_function(function_name, description)
config = fn.get_configuration() config = fn.get_configuration()
return 201, {}, json.dumps(config) return 201, {}, json.dumps(config)
@ -333,7 +329,7 @@ class LambdaResponse(BaseResponse):
function_name = unquote(self.path.rsplit("/", 1)[-1]) function_name = unquote(self.path.rsplit("/", 1)[-1])
qualifier = self._get_param("Qualifier", None) qualifier = self._get_param("Qualifier", None)
self.lambda_backend.delete_function(function_name, qualifier) self.backend.delete_function(function_name, qualifier)
return 204, {}, "" return 204, {}, ""
@staticmethod @staticmethod
@ -348,7 +344,7 @@ class LambdaResponse(BaseResponse):
function_name = unquote(self.path.rsplit("/", 1)[-1]) function_name = unquote(self.path.rsplit("/", 1)[-1])
qualifier = self._get_param("Qualifier", None) qualifier = self._get_param("Qualifier", None)
fn = self.lambda_backend.get_function(function_name, qualifier) fn = self.backend.get_function(function_name, qualifier)
code = fn.get_code() code = fn.get_code()
code["Configuration"] = self._set_configuration_qualifier( code["Configuration"] = self._set_configuration_qualifier(
@ -360,7 +356,7 @@ class LambdaResponse(BaseResponse):
function_name = unquote(self.path.rsplit("/", 2)[-2]) function_name = unquote(self.path.rsplit("/", 2)[-2])
qualifier = self._get_param("Qualifier", None) qualifier = self._get_param("Qualifier", None)
fn = self.lambda_backend.get_function(function_name, qualifier) fn = self.backend.get_function(function_name, qualifier)
configuration = self._set_configuration_qualifier( configuration = self._set_configuration_qualifier(
fn.get_configuration(), qualifier fn.get_configuration(), qualifier
@ -377,26 +373,26 @@ class LambdaResponse(BaseResponse):
def _list_tags(self): def _list_tags(self):
function_arn = unquote(self.path.rsplit("/", 1)[-1]) function_arn = unquote(self.path.rsplit("/", 1)[-1])
tags = self.lambda_backend.list_tags(function_arn) tags = self.backend.list_tags(function_arn)
return 200, {}, json.dumps({"Tags": tags}) return 200, {}, json.dumps({"Tags": tags})
def _tag_resource(self): def _tag_resource(self):
function_arn = unquote(self.path.rsplit("/", 1)[-1]) function_arn = unquote(self.path.rsplit("/", 1)[-1])
self.lambda_backend.tag_resource(function_arn, self.json_body["Tags"]) self.backend.tag_resource(function_arn, self.json_body["Tags"])
return 200, {}, "{}" return 200, {}, "{}"
def _untag_resource(self): def _untag_resource(self):
function_arn = unquote(self.path.rsplit("/", 1)[-1]) function_arn = unquote(self.path.rsplit("/", 1)[-1])
tag_keys = self.querystring["tagKeys"] tag_keys = self.querystring["tagKeys"]
self.lambda_backend.untag_resource(function_arn, tag_keys) self.backend.untag_resource(function_arn, tag_keys)
return 204, {}, "{}" return 204, {}, "{}"
def _put_configuration(self): def _put_configuration(self):
function_name = unquote(self.path.rsplit("/", 2)[-2]) function_name = unquote(self.path.rsplit("/", 2)[-2])
qualifier = self._get_param("Qualifier", None) qualifier = self._get_param("Qualifier", None)
resp = self.lambda_backend.update_function_configuration( resp = self.backend.update_function_configuration(
function_name, qualifier, body=self.json_body function_name, qualifier, body=self.json_body
) )
@ -408,7 +404,7 @@ class LambdaResponse(BaseResponse):
def _put_code(self): def _put_code(self):
function_name = unquote(self.path.rsplit("/", 2)[-2]) function_name = unquote(self.path.rsplit("/", 2)[-2])
qualifier = self._get_param("Qualifier", None) qualifier = self._get_param("Qualifier", None)
resp = self.lambda_backend.update_function_code( resp = self.backend.update_function_code(
function_name, qualifier, body=self.json_body function_name, qualifier, body=self.json_body
) )
@ -419,65 +415,63 @@ class LambdaResponse(BaseResponse):
def _get_code_signing_config(self): def _get_code_signing_config(self):
function_name = unquote(self.path.rsplit("/", 2)[-2]) function_name = unquote(self.path.rsplit("/", 2)[-2])
resp = self.lambda_backend.get_code_signing_config(function_name) resp = self.backend.get_code_signing_config(function_name)
return 200, {}, json.dumps(resp) return 200, {}, json.dumps(resp)
def _get_function_concurrency(self): def _get_function_concurrency(self):
path_function_name = unquote(self.path.rsplit("/", 2)[-2]) path_function_name = unquote(self.path.rsplit("/", 2)[-2])
function_name = self.lambda_backend.get_function(path_function_name) function_name = self.backend.get_function(path_function_name)
if function_name is None: if function_name is None:
return 404, {}, "{}" return 404, {}, "{}"
resp = self.lambda_backend.get_function_concurrency(path_function_name) resp = self.backend.get_function_concurrency(path_function_name)
return 200, {}, json.dumps({"ReservedConcurrentExecutions": resp}) return 200, {}, json.dumps({"ReservedConcurrentExecutions": resp})
def _delete_function_concurrency(self): def _delete_function_concurrency(self):
path_function_name = unquote(self.path.rsplit("/", 2)[-2]) path_function_name = unquote(self.path.rsplit("/", 2)[-2])
function_name = self.lambda_backend.get_function(path_function_name) function_name = self.backend.get_function(path_function_name)
if function_name is None: if function_name is None:
return 404, {}, "{}" return 404, {}, "{}"
self.lambda_backend.delete_function_concurrency(path_function_name) self.backend.delete_function_concurrency(path_function_name)
return 204, {}, "{}" return 204, {}, "{}"
def _put_function_concurrency(self): def _put_function_concurrency(self):
path_function_name = unquote(self.path.rsplit("/", 2)[-2]) path_function_name = unquote(self.path.rsplit("/", 2)[-2])
function = self.lambda_backend.get_function(path_function_name) function = self.backend.get_function(path_function_name)
if function is None: if function is None:
return 404, {}, "{}" return 404, {}, "{}"
concurrency = self._get_param("ReservedConcurrentExecutions", None) concurrency = self._get_param("ReservedConcurrentExecutions", None)
resp = self.lambda_backend.put_function_concurrency( resp = self.backend.put_function_concurrency(path_function_name, concurrency)
path_function_name, concurrency
)
return 200, {}, json.dumps({"ReservedConcurrentExecutions": resp}) return 200, {}, json.dumps({"ReservedConcurrentExecutions": resp})
def _list_layers(self): def _list_layers(self):
layers = self.lambda_backend.list_layers() layers = self.backend.list_layers()
return 200, {}, json.dumps({"Layers": layers}) return 200, {}, json.dumps({"Layers": layers})
def _delete_layer_version(self): def _delete_layer_version(self):
layer_name = self.path.split("/")[-3] layer_name = self.path.split("/")[-3]
layer_version = self.path.split("/")[-1] layer_version = self.path.split("/")[-1]
self.lambda_backend.delete_layer_version(layer_name, layer_version) self.backend.delete_layer_version(layer_name, layer_version)
return 200, {}, "{}" return 200, {}, "{}"
def _get_layer_version(self): def _get_layer_version(self):
layer_name = self.path.split("/")[-3] layer_name = self.path.split("/")[-3]
layer_version = self.path.split("/")[-1] layer_version = self.path.split("/")[-1]
layer = self.lambda_backend.get_layer_version(layer_name, layer_version) layer = self.backend.get_layer_version(layer_name, layer_version)
return 200, {}, json.dumps(layer.get_layer_version()) return 200, {}, json.dumps(layer.get_layer_version())
def _get_layer_versions(self): def _get_layer_versions(self):
layer_name = self.path.rsplit("/", 2)[-2] layer_name = self.path.rsplit("/", 2)[-2]
layer_versions = self.lambda_backend.get_layer_versions(layer_name) layer_versions = self.backend.get_layer_versions(layer_name)
return ( return (
200, 200,
{}, {},
@ -490,7 +484,7 @@ class LambdaResponse(BaseResponse):
spec = self.json_body spec = self.json_body
if "LayerName" not in spec: if "LayerName" not in spec:
spec["LayerName"] = self.path.rsplit("/", 2)[-2] spec["LayerName"] = self.path.rsplit("/", 2)[-2]
layer_version = self.lambda_backend.publish_layer_version(spec) layer_version = self.backend.publish_layer_version(spec)
config = layer_version.get_layer_version() config = layer_version.get_layer_version()
return 201, {}, json.dumps(config) return 201, {}, json.dumps(config)
@ -501,7 +495,7 @@ class LambdaResponse(BaseResponse):
description = params.get("Description", "") description = params.get("Description", "")
function_version = params.get("FunctionVersion") function_version = params.get("FunctionVersion")
routing_config = params.get("RoutingConfig") routing_config = params.get("RoutingConfig")
alias = self.lambda_backend.create_alias( alias = self.backend.create_alias(
name=alias_name, name=alias_name,
function_name=function_name, function_name=function_name,
function_version=function_version, function_version=function_version,
@ -513,15 +507,13 @@ class LambdaResponse(BaseResponse):
def _delete_alias(self): def _delete_alias(self):
function_name = unquote(self.path.rsplit("/")[-3]) function_name = unquote(self.path.rsplit("/")[-3])
alias_name = unquote(self.path.rsplit("/", 2)[-1]) alias_name = unquote(self.path.rsplit("/", 2)[-1])
self.lambda_backend.delete_alias(name=alias_name, function_name=function_name) self.backend.delete_alias(name=alias_name, function_name=function_name)
return 201, {}, "{}" return 201, {}, "{}"
def _get_alias(self): def _get_alias(self):
function_name = unquote(self.path.rsplit("/")[-3]) function_name = unquote(self.path.rsplit("/")[-3])
alias_name = unquote(self.path.rsplit("/", 2)[-1]) alias_name = unquote(self.path.rsplit("/", 2)[-1])
alias = self.lambda_backend.get_alias( alias = self.backend.get_alias(name=alias_name, function_name=function_name)
name=alias_name, function_name=function_name
)
return 201, {}, json.dumps(alias.to_json()) return 201, {}, json.dumps(alias.to_json())
def _update_alias(self): def _update_alias(self):
@ -531,7 +523,7 @@ class LambdaResponse(BaseResponse):
description = params.get("Description") description = params.get("Description")
function_version = params.get("FunctionVersion") function_version = params.get("FunctionVersion")
routing_config = params.get("RoutingConfig") routing_config = params.get("RoutingConfig")
alias = self.lambda_backend.update_alias( alias = self.backend.update_alias(
name=alias_name, name=alias_name,
function_name=function_name, function_name=function_name,
function_version=function_version, function_version=function_version,

View File

@ -26,8 +26,9 @@ def backends():
yield _import_backend(module_name, backends_name) yield _import_backend(module_name, backends_name)
def unique_backends(): def service_backends():
for module_name, backends_name in sorted(set(BACKENDS.values())): services = [(f.name, f.backend) for f in decorator_functions]
for module_name, backends_name in sorted(set(services)):
yield _import_backend(module_name, backends_name) yield _import_backend(module_name, backends_name)

View File

@ -9,7 +9,7 @@ import threading
import dateutil.parser import dateutil.parser
from sys import platform from sys import platform
from moto.core import BaseBackend, BaseModel, CloudFormationModel, get_account_id from moto.core import BaseBackend, BaseModel, CloudFormationModel
from moto.iam import iam_backends from moto.iam import iam_backends
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from moto.ecs import ecs_backends from moto.ecs import ecs_backends
@ -60,6 +60,7 @@ class ComputeEnvironment(CloudFormationModel):
state, state,
compute_resources, compute_resources,
service_role, service_role,
account_id,
region_name, region_name,
): ):
self.name = compute_environment_name self.name = compute_environment_name
@ -68,7 +69,7 @@ class ComputeEnvironment(CloudFormationModel):
self.compute_resources = compute_resources self.compute_resources = compute_resources
self.service_role = service_role self.service_role = service_role
self.arn = make_arn_for_compute_env( self.arn = make_arn_for_compute_env(
get_account_id(), compute_environment_name, region_name account_id, compute_environment_name, region_name
) )
self.instances = [] self.instances = []
@ -97,9 +98,9 @@ class ComputeEnvironment(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
backend = batch_backends[region_name] backend = batch_backends[account_id][region_name]
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
env = backend.create_compute_environment( env = backend.create_compute_environment(
@ -122,7 +123,6 @@ class JobQueue(CloudFormationModel):
state, state,
environments, environments,
env_order_json, env_order_json,
region_name,
backend, backend,
tags=None, tags=None,
): ):
@ -137,15 +137,13 @@ class JobQueue(CloudFormationModel):
:type environments: list of ComputeEnvironment :type environments: list of ComputeEnvironment
:param env_order_json: Compute Environments JSON for use when describing :param env_order_json: Compute Environments JSON for use when describing
:type env_order_json: list of dict :type env_order_json: list of dict
:param region_name: Region name
:type region_name: str
""" """
self.name = name self.name = name
self.priority = priority self.priority = priority
self.state = state self.state = state
self.environments = environments self.environments = environments
self.env_order_json = env_order_json self.env_order_json = env_order_json
self.arn = make_arn_for_job_queue(get_account_id(), name, region_name) self.arn = make_arn_for_job_queue(backend.account_id, name, backend.region_name)
self.status = "VALID" self.status = "VALID"
self.backend = backend self.backend = backend
@ -182,9 +180,9 @@ class JobQueue(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
backend = batch_backends[region_name] backend = batch_backends[account_id][region_name]
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
# Need to deal with difference case from cloudformation compute_resources, e.g. instanceRole vs InstanceRole # Need to deal with difference case from cloudformation compute_resources, e.g. instanceRole vs InstanceRole
@ -212,7 +210,6 @@ class JobDefinition(CloudFormationModel):
parameters, parameters,
_type, _type,
container_properties, container_properties,
region_name,
tags=None, tags=None,
revision=0, revision=0,
retry_strategy=0, retry_strategy=0,
@ -225,7 +222,7 @@ class JobDefinition(CloudFormationModel):
self.retry_strategy = retry_strategy self.retry_strategy = retry_strategy
self.type = _type self.type = _type
self.revision = revision self.revision = revision
self._region = region_name self._region = backend.region_name
self.container_properties = container_properties self.container_properties = container_properties
self.arn = None self.arn = None
self.status = "ACTIVE" self.status = "ACTIVE"
@ -257,7 +254,7 @@ class JobDefinition(CloudFormationModel):
def _update_arn(self): def _update_arn(self):
self.revision += 1 self.revision += 1
self.arn = make_arn_for_task_def( self.arn = make_arn_for_task_def(
get_account_id(), self.name, self.revision, self._region self.backend.account_id, self.name, self.revision, self._region
) )
def _get_resource_requirement(self, req_type, default=None): def _get_resource_requirement(self, req_type, default=None):
@ -347,7 +344,6 @@ class JobDefinition(CloudFormationModel):
parameters, parameters,
_type, _type,
container_properties, container_properties,
region_name=self._region,
revision=self.revision, revision=self.revision,
retry_strategy=retry_strategy, retry_strategy=retry_strategy,
tags=tags, tags=tags,
@ -392,9 +388,9 @@ class JobDefinition(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
backend = batch_backends[region_name] backend = batch_backends[account_id][region_name]
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
res = backend.register_job_definition( res = backend.register_job_definition(
def_name=resource_name, def_name=resource_name,
@ -844,7 +840,7 @@ class BatchBackend(BaseBackend):
:return: IAM Backend :return: IAM Backend
:rtype: moto.iam.models.IAMBackend :rtype: moto.iam.models.IAMBackend
""" """
return iam_backends["global"] return iam_backends[self.account_id]["global"]
@property @property
def ec2_backend(self): def ec2_backend(self):
@ -852,7 +848,7 @@ class BatchBackend(BaseBackend):
:return: EC2 Backend :return: EC2 Backend
:rtype: moto.ec2.models.EC2Backend :rtype: moto.ec2.models.EC2Backend
""" """
return ec2_backends[self.region_name] return ec2_backends[self.account_id][self.region_name]
@property @property
def ecs_backend(self): def ecs_backend(self):
@ -860,7 +856,7 @@ class BatchBackend(BaseBackend):
:return: ECS Backend :return: ECS Backend
:rtype: moto.ecs.models.EC2ContainerServiceBackend :rtype: moto.ecs.models.EC2ContainerServiceBackend
""" """
return ecs_backends[self.region_name] return ecs_backends[self.account_id][self.region_name]
@property @property
def logs_backend(self): def logs_backend(self):
@ -868,7 +864,7 @@ class BatchBackend(BaseBackend):
:return: ECS Backend :return: ECS Backend
:rtype: moto.logs.models.LogsBackend :rtype: moto.logs.models.LogsBackend
""" """
return logs_backends[self.region_name] return logs_backends[self.account_id][self.region_name]
def reset(self): def reset(self):
for job in self._jobs.values(): for job in self._jobs.values():
@ -1077,6 +1073,7 @@ class BatchBackend(BaseBackend):
state, state,
compute_resources, compute_resources,
service_role, service_role,
account_id=self.account_id,
region_name=self.region_name, region_name=self.region_name,
) )
self._compute_environments[new_comp_env.arn] = new_comp_env self._compute_environments[new_comp_env.arn] = new_comp_env
@ -1344,7 +1341,6 @@ class BatchBackend(BaseBackend):
state, state,
env_objects, env_objects,
compute_env_order, compute_env_order,
self.region_name,
backend=self, backend=self,
tags=tags, tags=tags,
) )
@ -1450,7 +1446,6 @@ class BatchBackend(BaseBackend):
_type, _type,
container_properties, container_properties,
tags=tags, tags=tags,
region_name=self.region_name,
retry_strategy=retry_strategy, retry_strategy=retry_strategy,
timeout=timeout, timeout=timeout,
backend=self, backend=self,

View File

@ -6,6 +6,9 @@ import json
class BatchResponse(BaseResponse): class BatchResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="batch")
def _error(self, code, message): def _error(self, code, message):
return json.dumps({"__type": code, "message": message}), dict(status=400) return json.dumps({"__type": code, "message": message}), dict(status=400)
@ -15,7 +18,7 @@ class BatchResponse(BaseResponse):
:return: Batch Backend :return: Batch Backend
:rtype: moto.batch.models.BatchBackend :rtype: moto.batch.models.BatchBackend
""" """
return batch_backends[self.region] return batch_backends[self.current_account][self.region]
@property @property
def json(self): def json(self):

View File

@ -12,7 +12,7 @@ class BatchSimpleBackend(BaseBackend):
@property @property
def backend(self): def backend(self):
return batch_backends[self.region_name] return batch_backends[self.account_id][self.region_name]
def __getattribute__(self, name): def __getattribute__(self, name):
""" """
@ -22,6 +22,7 @@ class BatchSimpleBackend(BaseBackend):
""" """
if name in [ if name in [
"backend", "backend",
"account_id",
"region_name", "region_name",
"urls", "urls",
"_url_module", "_url_module",

View File

@ -9,4 +9,4 @@ class BatchSimpleResponse(BatchResponse):
:return: Batch Backend :return: Batch Backend
:rtype: moto.batch.models.BatchBackend :rtype: moto.batch.models.BatchBackend
""" """
return batch_simple_backends[self.region] return batch_simple_backends[self.current_account][self.region]

View File

@ -5,9 +5,12 @@ from .models import budgets_backends
class BudgetsResponse(BaseResponse): class BudgetsResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="budgets")
@property @property
def backend(self): def backend(self):
return budgets_backends["global"] return budgets_backends[self.current_account]["global"]
def create_budget(self): def create_budget(self):
account_id = self._get_param("AccountId") account_id = self._get_param("AccountId")

View File

@ -1,19 +1,21 @@
"""CostExplorerBackend class with methods for supported APIs.""" """CostExplorerBackend class with methods for supported APIs."""
from .exceptions import CostCategoryNotFound from .exceptions import CostCategoryNotFound
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict from moto.core.utils import BackendDict
from uuid import uuid4 from uuid import uuid4
class CostCategoryDefinition(BaseModel): class CostCategoryDefinition(BaseModel):
def __init__(self, name, rule_version, rules, default_value, split_charge_rules): def __init__(
self, account_id, name, rule_version, rules, default_value, split_charge_rules
):
self.name = name self.name = name
self.rule_version = rule_version self.rule_version = rule_version
self.rules = rules self.rules = rules
self.default_value = default_value self.default_value = default_value
self.split_charge_rules = split_charge_rules self.split_charge_rules = split_charge_rules
self.arn = f"arn:aws:ce::{ACCOUNT_ID}:costcategory/{str(uuid4())}" self.arn = f"arn:aws:ce::{account_id}:costcategory/{str(uuid4())}"
def update(self, rule_version, rules, default_value, split_charge_rules): def update(self, rule_version, rules, default_value, split_charge_rules):
self.rule_version = rule_version self.rule_version = rule_version
@ -51,7 +53,12 @@ class CostExplorerBackend(BaseBackend):
The EffectiveOn and ResourceTags-parameters are not yet implemented The EffectiveOn and ResourceTags-parameters are not yet implemented
""" """
ccd = CostCategoryDefinition( ccd = CostCategoryDefinition(
name, rule_version, rules, default_value, split_charge_rules self.account_id,
name,
rule_version,
rules,
default_value,
split_charge_rules,
) )
self.cost_categories[ccd.arn] = ccd self.cost_categories[ccd.arn] = ccd
return ccd.arn, "" return ccd.arn, ""

View File

@ -11,7 +11,7 @@ class CostExplorerResponse(BaseResponse):
@property @property
def ce_backend(self): def ce_backend(self):
"""Return backend instance specific for this region.""" """Return backend instance specific for this region."""
return ce_backends["global"] return ce_backends[self.current_account]["global"]
def create_cost_category_definition(self): def create_cost_category_definition(self):
params = json.loads(self.body) params = json.loads(self.body)

View File

@ -33,7 +33,7 @@ class CustomModel(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
logical_id = kwargs["LogicalId"] logical_id = kwargs["LogicalId"]
stack_id = kwargs["StackId"] stack_id = kwargs["StackId"]
@ -41,7 +41,7 @@ class CustomModel(CloudFormationModel):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
service_token = properties["ServiceToken"] service_token = properties["ServiceToken"]
backend = lambda_backends[region_name] backend = lambda_backends[account_id][region_name]
fn = backend.get_function(service_token) fn = backend.get_function(service_token)
request_id = str(uuid4()) request_id = str(uuid4())
@ -52,7 +52,7 @@ class CustomModel(CloudFormationModel):
from moto.cloudformation import cloudformation_backends from moto.cloudformation import cloudformation_backends
stack = cloudformation_backends[region_name].get_stack(stack_id) stack = cloudformation_backends[account_id][region_name].get_stack(stack_id)
stack.add_custom_resource(custom_resource) stack.add_custom_resource(custom_resource)
# A request will be send to this URL to indicate success/failure # A request will be send to this URL to indicate success/failure

View File

@ -7,7 +7,7 @@ from collections import OrderedDict
from yaml.parser import ParserError # pylint:disable=c-extension-no-member from yaml.parser import ParserError # pylint:disable=c-extension-no-member
from yaml.scanner import ScannerError # pylint:disable=c-extension-no-member from yaml.scanner import ScannerError # pylint:disable=c-extension-no-member
from moto.core import BaseBackend, BaseModel, get_account_id from moto.core import BaseBackend, BaseModel
from moto.core.utils import ( from moto.core.utils import (
iso_8601_datetime_with_milliseconds, iso_8601_datetime_with_milliseconds,
iso_8601_datetime_without_milliseconds, iso_8601_datetime_without_milliseconds,
@ -31,6 +31,7 @@ class FakeStackSet(BaseModel):
def __init__( def __init__(
self, self,
stackset_id, stackset_id,
account_id,
name, name,
template, template,
region="us-east-1", region="us-east-1",
@ -42,13 +43,14 @@ class FakeStackSet(BaseModel):
execution_role="AWSCloudFormationStackSetExecutionRole", execution_role="AWSCloudFormationStackSetExecutionRole",
): ):
self.id = stackset_id self.id = stackset_id
self.arn = generate_stackset_arn(stackset_id, region) self.arn = generate_stackset_arn(stackset_id, region, account_id)
self.name = name self.name = name
self.template = template self.template = template
self.description = description self.description = description
self.parameters = parameters self.parameters = parameters
self.tags = tags self.tags = tags
self.admin_role = admin_role self.admin_role = admin_role
self.admin_role_arn = f"arn:aws:iam::{account_id}:role/{self.admin_role}"
self.execution_role = execution_role self.execution_role = execution_role
self.status = status self.status = status
self.instances = FakeStackInstances(parameters, self.id, self.name) self.instances = FakeStackInstances(parameters, self.id, self.name)
@ -218,6 +220,7 @@ class FakeStack(BaseModel):
name, name,
template, template,
parameters, parameters,
account_id,
region_name, region_name,
notification_arns=None, notification_arns=None,
tags=None, tags=None,
@ -226,6 +229,7 @@ class FakeStack(BaseModel):
): ):
self.stack_id = stack_id self.stack_id = stack_id
self.name = name self.name = name
self.account_id = account_id
self.template = template self.template = template
if template != {}: if template != {}:
self._parse_template() self._parse_template()
@ -267,9 +271,10 @@ class FakeStack(BaseModel):
self.name, self.name,
self.parameters, self.parameters,
self.tags, self.tags,
self.region_name, account_id=self.account_id,
self.template_dict, region_name=self.region_name,
self.cross_stack_resources, template=self.template_dict,
cross_stack_resources=self.cross_stack_resources,
) )
resource_map.load() resource_map.load()
return resource_map return resource_map
@ -296,7 +301,7 @@ class FakeStack(BaseModel):
resource_properties=resource_properties, resource_properties=resource_properties,
) )
event.sendToSns(self.region_name, self.notification_arns) event.sendToSns(self.account_id, self.region_name, self.notification_arns)
self.events.append(event) self.events.append(event)
def _add_resource_event( def _add_resource_event(
@ -486,7 +491,7 @@ class FakeEvent(BaseModel):
self.event_id = uuid.uuid4() self.event_id = uuid.uuid4()
self.client_request_token = client_request_token self.client_request_token = client_request_token
def sendToSns(self, region, sns_topic_arns): def sendToSns(self, account_id, region, sns_topic_arns):
message = """StackId='{stack_id}' message = """StackId='{stack_id}'
Timestamp='{timestamp}' Timestamp='{timestamp}'
EventId='{event_id}' EventId='{event_id}'
@ -502,7 +507,7 @@ ClientRequestToken='{client_request_token}'""".format(
timestamp=iso_8601_datetime_with_milliseconds(self.timestamp), timestamp=iso_8601_datetime_with_milliseconds(self.timestamp),
event_id=self.event_id, event_id=self.event_id,
logical_resource_id=self.logical_resource_id, logical_resource_id=self.logical_resource_id,
account_id=get_account_id(), account_id=account_id,
resource_properties=self.resource_properties, resource_properties=self.resource_properties,
resource_status=self.resource_status, resource_status=self.resource_status,
resource_status_reason=self.resource_status_reason, resource_status_reason=self.resource_status_reason,
@ -512,7 +517,7 @@ ClientRequestToken='{client_request_token}'""".format(
) )
for sns_topic_arn in sns_topic_arns: for sns_topic_arn in sns_topic_arns:
sns_backends[region].publish( sns_backends[account_id][region].publish(
message, subject="AWS CloudFormation Notification", arn=sns_topic_arn message, subject="AWS CloudFormation Notification", arn=sns_topic_arn
) )
@ -584,6 +589,7 @@ class CloudFormationBackend(BaseBackend):
stackset_id = generate_stackset_id(name) stackset_id = generate_stackset_id(name)
new_stackset = FakeStackSet( new_stackset = FakeStackSet(
stackset_id=stackset_id, stackset_id=stackset_id,
account_id=self.account_id,
name=name, name=name,
template=template, template=template,
parameters=parameters, parameters=parameters,
@ -671,12 +677,13 @@ class CloudFormationBackend(BaseBackend):
tags=None, tags=None,
role_arn=None, role_arn=None,
): ):
stack_id = generate_stack_id(name, self.region_name) stack_id = generate_stack_id(name, self.region_name, self.account_id)
new_stack = FakeStack( new_stack = FakeStack(
stack_id=stack_id, stack_id=stack_id,
name=name, name=name,
template=template, template=template,
parameters=parameters, parameters=parameters,
account_id=self.account_id,
region_name=self.region_name, region_name=self.region_name,
notification_arns=notification_arns, notification_arns=notification_arns,
tags=tags, tags=tags,
@ -712,12 +719,13 @@ class CloudFormationBackend(BaseBackend):
else: else:
raise ValidationError(stack_name) raise ValidationError(stack_name)
else: else:
stack_id = generate_stack_id(stack_name, self.region_name) stack_id = generate_stack_id(stack_name, self.region_name, self.account_id)
stack = FakeStack( stack = FakeStack(
stack_id=stack_id, stack_id=stack_id,
name=stack_name, name=stack_name,
template={}, template={},
parameters=parameters, parameters=parameters,
account_id=self.account_id,
region_name=self.region_name, region_name=self.region_name,
notification_arns=notification_arns, notification_arns=notification_arns,
tags=tags, tags=tags,
@ -729,7 +737,9 @@ class CloudFormationBackend(BaseBackend):
"REVIEW_IN_PROGRESS", resource_status_reason="User Initiated" "REVIEW_IN_PROGRESS", resource_status_reason="User Initiated"
) )
change_set_id = generate_changeset_id(change_set_name, self.region_name) change_set_id = generate_changeset_id(
change_set_name, self.region_name, self.account_id
)
new_change_set = FakeChangeSet( new_change_set = FakeChangeSet(
change_set_type=change_set_type, change_set_type=change_set_type,

View File

@ -46,7 +46,7 @@ from moto.ssm import models # noqa # pylint: disable=all
# End ugly list of imports # End ugly list of imports
from moto.core import get_account_id, CloudFormationModel from moto.core import CloudFormationModel
from moto.s3.models import s3_backends from moto.s3.models import s3_backends
from moto.s3.utils import bucket_and_name_from_url from moto.s3.utils import bucket_and_name_from_url
from moto.ssm import ssm_backends from moto.ssm import ssm_backends
@ -317,7 +317,9 @@ def parse_resource_and_generate_name(logical_id, resource_json, resources_map):
return resource_class, resource_json, resource_name return resource_class, resource_json, resource_name
def parse_and_create_resource(logical_id, resource_json, resources_map, region_name): def parse_and_create_resource(
logical_id, resource_json, resources_map, account_id, region_name
):
condition = resource_json.get("Condition") condition = resource_json.get("Condition")
if condition and not resources_map.lazy_condition_map[condition]: if condition and not resources_map.lazy_condition_map[condition]:
# If this has a False condition, don't create the resource # If this has a False condition, don't create the resource
@ -336,14 +338,16 @@ def parse_and_create_resource(logical_id, resource_json, resources_map, region_n
"ResourceType": resource_type, "ResourceType": resource_type,
} }
resource = resource_class.create_from_cloudformation_json( resource = resource_class.create_from_cloudformation_json(
resource_physical_name, resource_json, region_name, **kwargs resource_physical_name, resource_json, account_id, region_name, **kwargs
) )
resource.type = resource_type resource.type = resource_type
resource.logical_resource_id = logical_id resource.logical_resource_id = logical_id
return resource return resource
def parse_and_update_resource(logical_id, resource_json, resources_map, region_name): def parse_and_update_resource(
logical_id, resource_json, resources_map, account_id, region_name
):
resource_tuple = parse_resource_and_generate_name( resource_tuple = parse_resource_and_generate_name(
logical_id, resource_json, resources_map logical_id, resource_json, resources_map
) )
@ -358,6 +362,7 @@ def parse_and_update_resource(logical_id, resource_json, resources_map, region_n
original_resource=original_resource, original_resource=original_resource,
new_resource_name=new_resource_name, new_resource_name=new_resource_name,
cloudformation_json=resource_json, cloudformation_json=resource_json,
account_id=account_id,
region_name=region_name, region_name=region_name,
) )
new_resource.type = resource_json["Type"] new_resource.type = resource_json["Type"]
@ -367,14 +372,14 @@ def parse_and_update_resource(logical_id, resource_json, resources_map, region_n
return None return None
def parse_and_delete_resource(resource_name, resource_json, region_name): def parse_and_delete_resource(resource_name, resource_json, account_id, region_name):
resource_type = resource_json["Type"] resource_type = resource_json["Type"]
resource_class = resource_class_from_type(resource_type) resource_class = resource_class_from_type(resource_type)
if not hasattr( if not hasattr(
resource_class.delete_from_cloudformation_json, "__isabstractmethod__" resource_class.delete_from_cloudformation_json, "__isabstractmethod__"
): ):
resource_class.delete_from_cloudformation_json( resource_class.delete_from_cloudformation_json(
resource_name, resource_json, region_name resource_name, resource_json, account_id, region_name
) )
@ -439,11 +444,13 @@ class ResourceMap(collections_abc.Mapping):
parameters, parameters,
tags, tags,
region_name, region_name,
account_id,
template, template,
cross_stack_resources, cross_stack_resources,
): ):
self._template = template self._template = template
self._resource_json_map = template["Resources"] if template != {} else {} self._resource_json_map = template["Resources"] if template != {} else {}
self._account_id = account_id
self._region_name = region_name self._region_name = region_name
self.input_parameters = parameters self.input_parameters = parameters
self.tags = copy.deepcopy(tags) self.tags = copy.deepcopy(tags)
@ -453,7 +460,7 @@ class ResourceMap(collections_abc.Mapping):
# Create the default resources # Create the default resources
self._parsed_resources = { self._parsed_resources = {
"AWS::AccountId": get_account_id(), "AWS::AccountId": account_id,
"AWS::Region": self._region_name, "AWS::Region": self._region_name,
"AWS::StackId": stack_id, "AWS::StackId": stack_id,
"AWS::StackName": stack_name, "AWS::StackName": stack_name,
@ -473,7 +480,11 @@ class ResourceMap(collections_abc.Mapping):
if not resource_json: if not resource_json:
raise KeyError(resource_logical_id) raise KeyError(resource_logical_id)
new_resource = parse_and_create_resource( new_resource = parse_and_create_resource(
resource_logical_id, resource_json, self, self._region_name resource_logical_id,
resource_json,
self,
account_id=self._account_id,
region_name=self._region_name,
) )
if new_resource is not None: if new_resource is not None:
self._parsed_resources[resource_logical_id] = new_resource self._parsed_resources[resource_logical_id] = new_resource
@ -528,14 +539,18 @@ class ResourceMap(collections_abc.Mapping):
if name == "AWS::Include": if name == "AWS::Include":
location = params["Location"] location = params["Location"]
bucket_name, name = bucket_and_name_from_url(location) bucket_name, name = bucket_and_name_from_url(location)
key = s3_backends["global"].get_object(bucket_name, name) key = s3_backends[self._account_id]["global"].get_object(
bucket_name, name
)
self._parsed_resources.update(json.loads(key.value)) self._parsed_resources.update(json.loads(key.value))
def parse_ssm_parameter(self, value, value_type): def parse_ssm_parameter(self, value, value_type):
# The Value in SSM parameters is the SSM parameter path # The Value in SSM parameters is the SSM parameter path
# we need to use ssm_backend to retrieve the # we need to use ssm_backend to retrieve the
# actual value from parameter store # actual value from parameter store
parameter = ssm_backends[self._region_name].get_parameter(value) parameter = ssm_backends[self._account_id][self._region_name].get_parameter(
value
)
actual_value = parameter.value actual_value = parameter.value
if value_type.find("List") > 0: if value_type.find("List") > 0:
return actual_value.split(",") return actual_value.split(",")
@ -646,9 +661,9 @@ class ResourceMap(collections_abc.Mapping):
instance = self[resource] instance = self[resource]
if isinstance(instance, TaggedEC2Resource): if isinstance(instance, TaggedEC2Resource):
self.tags["aws:cloudformation:logical-id"] = resource self.tags["aws:cloudformation:logical-id"] = resource
ec2_models.ec2_backends[self._region_name].create_tags( ec2_models.ec2_backends[self._account_id][
[instance.physical_resource_id], self.tags self._region_name
) ].create_tags([instance.physical_resource_id], self.tags)
if instance and not instance.is_created(): if instance and not instance.is_created():
all_resources_ready = False all_resources_ready = False
return all_resources_ready return all_resources_ready
@ -716,7 +731,9 @@ class ResourceMap(collections_abc.Mapping):
].physical_resource_id ].physical_resource_id
else: else:
resource_name = None resource_name = None
parse_and_delete_resource(resource_name, resource_json, self._region_name) parse_and_delete_resource(
resource_name, resource_json, self._account_id, self._region_name
)
self._parsed_resources.pop(logical_name) self._parsed_resources.pop(logical_name)
self._template = template self._template = template
@ -740,7 +757,11 @@ class ResourceMap(collections_abc.Mapping):
resource_json = self._resource_json_map[logical_name] resource_json = self._resource_json_map[logical_name]
try: try:
changed_resource = parse_and_update_resource( changed_resource = parse_and_update_resource(
logical_name, resource_json, self, self._region_name logical_name,
resource_json,
self,
account_id=self._account_id,
region_name=self._region_name,
) )
except Exception as e: except Exception as e:
# skip over dependency violations, and try again in a # skip over dependency violations, and try again in a
@ -765,7 +786,7 @@ class ResourceMap(collections_abc.Mapping):
and parsed_resource is not None and parsed_resource is not None
): ):
if parsed_resource and hasattr(parsed_resource, "delete"): if parsed_resource and hasattr(parsed_resource, "delete"):
parsed_resource.delete(self._region_name) parsed_resource.delete(self._account_id, self._region_name)
else: else:
if hasattr(parsed_resource, "physical_resource_id"): if hasattr(parsed_resource, "physical_resource_id"):
resource_name = parsed_resource.physical_resource_id resource_name = parsed_resource.physical_resource_id
@ -777,7 +798,10 @@ class ResourceMap(collections_abc.Mapping):
] ]
parse_and_delete_resource( parse_and_delete_resource(
resource_name, resource_json, self._region_name resource_name,
resource_json,
self._account_id,
self._region_name,
) )
self._parsed_resources.pop(parsed_resource.logical_resource_id) self._parsed_resources.pop(parsed_resource.logical_resource_id)

View File

@ -8,7 +8,6 @@ from moto.core.responses import BaseResponse
from moto.core.utils import amzn_request_id from moto.core.utils import amzn_request_id
from moto.s3.models import s3_backends from moto.s3.models import s3_backends
from moto.s3.exceptions import S3ClientError from moto.s3.exceptions import S3ClientError
from moto.core import get_account_id
from .models import cloudformation_backends from .models import cloudformation_backends
from .exceptions import ValidationError, MissingParameterError from .exceptions import ValidationError, MissingParameterError
from .utils import yaml_tag_constructor from .utils import yaml_tag_constructor
@ -39,9 +38,12 @@ def get_template_summary_response_from_template(template_body):
class CloudFormationResponse(BaseResponse): class CloudFormationResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="cloudformation")
@property @property
def cloudformation_backend(self): def cloudformation_backend(self):
return cloudformation_backends[self.region] return cloudformation_backends[self.current_account][self.region]
@classmethod @classmethod
def cfnresponse(cls, *args, **kwargs): # pylint: disable=unused-argument def cfnresponse(cls, *args, **kwargs): # pylint: disable=unused-argument
@ -68,7 +70,9 @@ class CloudFormationResponse(BaseResponse):
bucket_name = template_url_parts.netloc.split(".")[0] bucket_name = template_url_parts.netloc.split(".")[0]
key_name = template_url_parts.path.lstrip("/") key_name = template_url_parts.path.lstrip("/")
key = s3_backends["global"].get_object(bucket_name, key_name) key = s3_backends[self.current_account]["global"].get_object(
bucket_name, key_name
)
return key.value.decode("utf-8") return key.value.decode("utf-8")
def _get_params_from_list(self, parameters_list): def _get_params_from_list(self, parameters_list):
@ -515,9 +519,7 @@ class CloudFormationResponse(BaseResponse):
stackset = self.cloudformation_backend.get_stack_set(stackset_name) stackset = self.cloudformation_backend.get_stack_set(stackset_name)
if not stackset.admin_role: if not stackset.admin_role:
stackset.admin_role = "arn:aws:iam::{AccountId}:role/AWSCloudFormationStackSetAdministrationRole".format( stackset.admin_role = f"arn:aws:iam::{self.current_account}:role/AWSCloudFormationStackSetAdministrationRole"
AccountId=get_account_id()
)
if not stackset.execution_role: if not stackset.execution_role:
stackset.execution_role = "AWSCloudFormationStackSetExecutionRole" stackset.execution_role = "AWSCloudFormationStackSetExecutionRole"
@ -1169,14 +1171,11 @@ STOP_STACK_SET_OPERATION_RESPONSE_TEMPLATE = """<StopStackSetOperationResponse x
</StopStackSetOperationResponse> </StopStackSetOperationResponse>
""" """
DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = ( DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = """<DescribeStackSetOperationResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
"""<DescribeStackSetOperationResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<DescribeStackSetOperationResult> <DescribeStackSetOperationResult>
<StackSetOperation> <StackSetOperation>
<ExecutionRoleName>{{ stackset.execution_role }}</ExecutionRoleName> <ExecutionRoleName>{{ stackset.execution_role }}</ExecutionRoleName>
<AdministrationRoleARN>arn:aws:iam::""" <AdministrationRoleARN>{{ stackset.admin_role_arn }}</AdministrationRoleARN>
+ get_account_id()
+ """:role/{{ stackset.admin_role }}</AdministrationRoleARN>
<StackSetId>{{ stackset.id }}</StackSetId> <StackSetId>{{ stackset.id }}</StackSetId>
<CreationTimestamp>{{ operation.CreationTimestamp }}</CreationTimestamp> <CreationTimestamp>{{ operation.CreationTimestamp }}</CreationTimestamp>
<OperationId>{{ operation.OperationId }}</OperationId> <OperationId>{{ operation.OperationId }}</OperationId>
@ -1193,19 +1192,15 @@ DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = (
</ResponseMetadata> </ResponseMetadata>
</DescribeStackSetOperationResponse> </DescribeStackSetOperationResponse>
""" """
)
LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = ( LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = """<ListStackSetOperationResultsResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
"""<ListStackSetOperationResultsResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
<ListStackSetOperationResultsResult> <ListStackSetOperationResultsResult>
<Summaries> <Summaries>
{% for instance in operation.Instances %} {% for instance in operation.Instances %}
{% for account, region in instance.items() %} {% for account, region in instance.items() %}
<member> <member>
<AccountGateResult> <AccountGateResult>
<StatusReason>Function not found: arn:aws:lambda:us-west-2:""" <StatusReason>Function not found: arn:aws:lambda:us-west-2:{{ account }}:function:AWSCloudFormationStackSetAccountGate</StatusReason>
+ get_account_id()
+ """:function:AWSCloudFormationStackSetAccountGate</StatusReason>
<Status>SKIPPED</Status> <Status>SKIPPED</Status>
</AccountGateResult> </AccountGateResult>
<Region>{{ region }}</Region> <Region>{{ region }}</Region>
@ -1221,7 +1216,6 @@ LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = (
</ResponseMetadata> </ResponseMetadata>
</ListStackSetOperationResultsResponse> </ListStackSetOperationResultsResponse>
""" """
)
# https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_GetTemplateSummary.html # https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_GetTemplateSummary.html
# TODO:implement fields: ResourceIdentifierSummaries, Capabilities, CapabilitiesReason # TODO:implement fields: ResourceIdentifierSummaries, Capabilities, CapabilitiesReason

View File

@ -4,21 +4,15 @@ import yaml
import os import os
import string import string
from moto.core import get_account_id
def generate_stack_id(stack_name, region, account):
def generate_stack_id(stack_name, region="us-east-1", account=get_account_id()):
random_id = uuid.uuid4() random_id = uuid.uuid4()
return "arn:aws:cloudformation:{}:{}:stack/{}/{}".format( return f"arn:aws:cloudformation:{region}:{account}:stack/{stack_name}/{random_id}"
region, account, stack_name, random_id
)
def generate_changeset_id(changeset_name, region_name): def generate_changeset_id(changeset_name, region_name, account_id):
random_id = uuid.uuid4() random_id = uuid.uuid4()
return "arn:aws:cloudformation:{0}:{1}:changeSet/{2}/{3}".format( return f"arn:aws:cloudformation:{region_name}:{account_id}:changeSet/{changeset_name}/{random_id}"
region_name, get_account_id(), changeset_name, random_id
)
def generate_stackset_id(stackset_name): def generate_stackset_id(stackset_name):
@ -26,10 +20,8 @@ def generate_stackset_id(stackset_name):
return "{}:{}".format(stackset_name, random_id) return "{}:{}".format(stackset_name, random_id)
def generate_stackset_arn(stackset_id, region_name): def generate_stackset_arn(stackset_id, region_name, account_id):
return "arn:aws:cloudformation:{}:{}:stackset/{}".format( return f"arn:aws:cloudformation:{region_name}:{account_id}:stackset/{stackset_id}"
region_name, get_account_id(), stackset_id
)
def random_suffix(): def random_suffix():

View File

@ -2,7 +2,7 @@ import random
import string import string
from datetime import datetime from datetime import datetime
from moto.core import get_account_id, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict, iso_8601_datetime_with_milliseconds from moto.core.utils import BackendDict, iso_8601_datetime_with_milliseconds
from moto.moto_api import state_manager from moto.moto_api import state_manager
from moto.moto_api._internal.managed_state_model import ManagedState from moto.moto_api._internal.managed_state_model import ManagedState
@ -181,7 +181,7 @@ class Distribution(BaseModel, ManagedState):
) )
return resource_id return resource_id
def __init__(self, config): def __init__(self, account_id, config):
# Configured ManagedState # Configured ManagedState
super().__init__( super().__init__(
"cloudfront::distribution", transitions=[("InProgress", "Deployed")] "cloudfront::distribution", transitions=[("InProgress", "Deployed")]
@ -189,7 +189,7 @@ class Distribution(BaseModel, ManagedState):
# Configure internal properties # Configure internal properties
self.distribution_id = Distribution.random_id() self.distribution_id = Distribution.random_id()
self.arn = ( self.arn = (
f"arn:aws:cloudfront:{get_account_id()}:distribution/{self.distribution_id}" f"arn:aws:cloudfront:{account_id}:distribution/{self.distribution_id}"
) )
self.distribution_config = DistributionConfig(config) self.distribution_config = DistributionConfig(config)
self.active_trusted_signers = ActiveTrustedSigners() self.active_trusted_signers = ActiveTrustedSigners()
@ -247,7 +247,7 @@ class CloudFrontBackend(BaseBackend):
we're not persisting/returning the correct attributes for your we're not persisting/returning the correct attributes for your
use-case. use-case.
""" """
dist = Distribution(distribution_config) dist = Distribution(self.account_id, distribution_config)
caller_reference = dist.distribution_config.caller_reference caller_reference = dist.distribution_config.caller_reference
existing_dist = self._distribution_with_caller_reference(caller_reference) existing_dist = self._distribution_with_caller_reference(caller_reference)
if existing_dist: if existing_dist:

View File

@ -9,12 +9,15 @@ XMLNS = "http://cloudfront.amazonaws.com/doc/2020-05-31/"
class CloudFrontResponse(BaseResponse): class CloudFrontResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="cloudfront")
def _get_xml_body(self): def _get_xml_body(self):
return xmltodict.parse(self.body, dict_constructor=dict) return xmltodict.parse(self.body, dict_constructor=dict)
@property @property
def backend(self): def backend(self):
return cloudfront_backends["global"] return cloudfront_backends[self.current_account]["global"]
def distributions(self, request, full_url, headers): def distributions(self, request, full_url, headers):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)

View File

@ -1,5 +1,4 @@
"""Exceptions raised by the cloudtrail service.""" """Exceptions raised by the cloudtrail service."""
from moto.core import get_account_id
from moto.core.exceptions import JsonRESTError from moto.core.exceptions import JsonRESTError
@ -27,10 +26,10 @@ class InsufficientSnsTopicPolicyException(JsonRESTError):
class TrailNotFoundException(JsonRESTError): class TrailNotFoundException(JsonRESTError):
code = 400 code = 400
def __init__(self, name): def __init__(self, account_id, name):
super().__init__( super().__init__(
"TrailNotFoundException", "TrailNotFoundException",
f"Unknown trail: {name} for the user: {get_account_id()}", f"Unknown trail: {name} for the user: {account_id}",
) )

View File

@ -2,7 +2,7 @@ import re
import time import time
from datetime import datetime from datetime import datetime
from moto.core import get_account_id, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_without_milliseconds, BackendDict from moto.core.utils import iso_8601_datetime_without_milliseconds, BackendDict
from moto.utilities.tagging_service import TaggingService from moto.utilities.tagging_service import TaggingService
from .exceptions import ( from .exceptions import (
@ -74,6 +74,7 @@ class TrailStatus(object):
class Trail(BaseModel): class Trail(BaseModel):
def __init__( def __init__(
self, self,
account_id,
region_name, region_name,
trail_name, trail_name,
bucket_name, bucket_name,
@ -87,6 +88,7 @@ class Trail(BaseModel):
cw_role_arn, cw_role_arn,
kms_key_id, kms_key_id,
): ):
self.account_id = account_id
self.region_name = region_name self.region_name = region_name
self.trail_name = trail_name self.trail_name = trail_name
self.bucket_name = bucket_name self.bucket_name = bucket_name
@ -109,12 +111,12 @@ class Trail(BaseModel):
@property @property
def arn(self): def arn(self):
return f"arn:aws:cloudtrail:{self.region_name}:{get_account_id()}:trail/{self.trail_name}" return f"arn:aws:cloudtrail:{self.region_name}:{self.account_id}:trail/{self.trail_name}"
@property @property
def topic_arn(self): def topic_arn(self):
if self.sns_topic_name: if self.sns_topic_name:
return f"arn:aws:sns:{self.region_name}:{get_account_id()}:{self.sns_topic_name}" return f"arn:aws:sns:{self.region_name}:{self.account_id}:{self.sns_topic_name}"
return None return None
def check_name(self): def check_name(self):
@ -133,7 +135,7 @@ class Trail(BaseModel):
from moto.s3.models import s3_backends from moto.s3.models import s3_backends
try: try:
s3_backends["global"].get_bucket(self.bucket_name) s3_backends[self.account_id]["global"].get_bucket(self.bucket_name)
except Exception: except Exception:
raise S3BucketDoesNotExistException( raise S3BucketDoesNotExistException(
f"S3 bucket {self.bucket_name} does not exist!" f"S3 bucket {self.bucket_name} does not exist!"
@ -143,7 +145,7 @@ class Trail(BaseModel):
if self.sns_topic_name: if self.sns_topic_name:
from moto.sns import sns_backends from moto.sns import sns_backends
sns_backend = sns_backends[self.region_name] sns_backend = sns_backends[self.account_id][self.region_name]
try: try:
sns_backend.get_topic(self.topic_arn) sns_backend.get_topic(self.topic_arn)
except Exception: except Exception:
@ -263,6 +265,7 @@ class CloudTrailBackend(BaseBackend):
tags_list, tags_list,
): ):
trail = Trail( trail = Trail(
self.account_id,
self.region_name, self.region_name,
name, name,
bucket_name, bucket_name,
@ -288,7 +291,7 @@ class CloudTrailBackend(BaseBackend):
for trail in self.trails.values(): for trail in self.trails.values():
if trail.arn == name_or_arn: if trail.arn == name_or_arn:
return trail return trail
raise TrailNotFoundException(name_or_arn) raise TrailNotFoundException(account_id=self.account_id, name=name_or_arn)
def get_trail_status(self, name): def get_trail_status(self, name):
if len(name) < 3: if len(name) < 3:
@ -304,9 +307,9 @@ class CloudTrailBackend(BaseBackend):
if not trail_name: if not trail_name:
# This particular method returns the ARN as part of the error message # This particular method returns the ARN as part of the error message
arn = ( arn = (
f"arn:aws:cloudtrail:{self.region_name}:{get_account_id()}:trail/{name}" f"arn:aws:cloudtrail:{self.region_name}:{self.account_id}:trail/{name}"
) )
raise TrailNotFoundException(name=arn) raise TrailNotFoundException(account_id=self.account_id, name=arn)
trail = self.trails[trail_name] trail = self.trails[trail_name]
return trail.status return trail.status

View File

@ -9,10 +9,13 @@ from .exceptions import InvalidParameterCombinationException
class CloudTrailResponse(BaseResponse): class CloudTrailResponse(BaseResponse):
"""Handler for CloudTrail requests and responses.""" """Handler for CloudTrail requests and responses."""
def __init__(self):
super().__init__(service_name="cloudtrail")
@property @property
def cloudtrail_backend(self): def cloudtrail_backend(self):
"""Return backend instance specific for this region.""" """Return backend instance specific for this region."""
return cloudtrail_backends[self.region] return cloudtrail_backends[self.current_account][self.region]
def create_trail(self): def create_trail(self):
name = self._get_param("Name") name = self._get_param("Name")

View File

@ -20,7 +20,6 @@ from .exceptions import (
from .utils import make_arn_for_dashboard, make_arn_for_alarm from .utils import make_arn_for_dashboard, make_arn_for_alarm
from dateutil import parser from dateutil import parser
from moto.core import get_account_id
from ..utilities.tagging_service import TaggingService from ..utilities.tagging_service import TaggingService
_EMPTY_LIST = tuple() _EMPTY_LIST = tuple()
@ -103,6 +102,7 @@ def daterange(start, stop, step=timedelta(days=1), inclusive=False):
class FakeAlarm(BaseModel): class FakeAlarm(BaseModel):
def __init__( def __init__(
self, self,
account_id,
region_name, region_name,
name, name,
namespace, namespace,
@ -129,7 +129,7 @@ class FakeAlarm(BaseModel):
): ):
self.region_name = region_name self.region_name = region_name
self.name = name self.name = name
self.alarm_arn = make_arn_for_alarm(region_name, get_account_id(), name) self.alarm_arn = make_arn_for_alarm(region_name, account_id, name)
self.namespace = namespace self.namespace = namespace
self.metric_name = metric_name self.metric_name = metric_name
self.metric_data_queries = metric_data_queries self.metric_data_queries = metric_data_queries
@ -238,9 +238,9 @@ class MetricDatum(BaseModel):
class Dashboard(BaseModel): class Dashboard(BaseModel):
def __init__(self, name, body): def __init__(self, account_id, name, body):
# Guaranteed to be unique for now as the name is also the key of a dictionary where they are stored # Guaranteed to be unique for now as the name is also the key of a dictionary where they are stored
self.arn = make_arn_for_dashboard(get_account_id(), name) self.arn = make_arn_for_dashboard(account_id, name)
self.name = name self.name = name
self.body = body self.body = body
self.last_modified = datetime.now() self.last_modified = datetime.now()
@ -327,7 +327,7 @@ class CloudWatchBackend(BaseBackend):
providers = CloudWatchMetricProvider.__subclasses__() providers = CloudWatchMetricProvider.__subclasses__()
md = [] md = []
for provider in providers: for provider in providers:
md.extend(provider.get_cloudwatch_metrics()) md.extend(provider.get_cloudwatch_metrics(self.account_id))
return md return md
def put_metric_alarm( def put_metric_alarm(
@ -370,6 +370,7 @@ class CloudWatchBackend(BaseBackend):
) )
alarm = FakeAlarm( alarm = FakeAlarm(
account_id=self.account_id,
region_name=self.region_name, region_name=self.region_name,
name=name, name=name,
namespace=namespace, namespace=namespace,
@ -590,7 +591,7 @@ class CloudWatchBackend(BaseBackend):
return self.metric_data + self.aws_metric_data return self.metric_data + self.aws_metric_data
def put_dashboard(self, name, body): def put_dashboard(self, name, body):
self.dashboards[name] = Dashboard(name, body) self.dashboards[name] = Dashboard(self.account_id, name, body)
def list_dashboards(self, prefix=""): def list_dashboards(self, prefix=""):
for key, value in self.dashboards.items(): for key, value in self.dashboards.items():

View File

@ -9,9 +9,12 @@ from .exceptions import InvalidParameterCombination
class CloudWatchResponse(BaseResponse): class CloudWatchResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="cloudwatch")
@property @property
def cloudwatch_backend(self): def cloudwatch_backend(self):
return cloudwatch_backends[self.region] return cloudwatch_backends[self.current_account][self.region]
def _error(self, code, message, status=400): def _error(self, code, message, status=400):
template = self.response_template(ERROR_RESPONSE_TEMPLATE) template = self.response_template(ERROR_RESPONSE_TEMPLATE)

View File

@ -1,6 +1,5 @@
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds, BackendDict from moto.core.utils import iso_8601_datetime_with_milliseconds, BackendDict
from moto.core import get_account_id
from collections import defaultdict from collections import defaultdict
from random import randint from random import randint
from dateutil import parser from dateutil import parser
@ -9,14 +8,23 @@ import uuid
class CodeBuildProjectMetadata(BaseModel): class CodeBuildProjectMetadata(BaseModel):
def __init__(self, project_name, source_version, artifacts, build_id, service_role): def __init__(
self,
account_id,
region_name,
project_name,
source_version,
artifacts,
build_id,
service_role,
):
current_date = iso_8601_datetime_with_milliseconds(datetime.datetime.utcnow()) current_date = iso_8601_datetime_with_milliseconds(datetime.datetime.utcnow())
self.build_metadata = dict() self.build_metadata = dict()
self.build_metadata["id"] = build_id self.build_metadata["id"] = build_id
self.build_metadata["arn"] = "arn:aws:codebuild:eu-west-2:{0}:build/{1}".format( self.build_metadata[
get_account_id(), build_id "arn"
) ] = f"arn:aws:codebuild:{region_name}:{account_id}:build/{build_id}"
self.build_metadata["buildNumber"] = randint(1, 100) self.build_metadata["buildNumber"] = randint(1, 100)
self.build_metadata["startTime"] = current_date self.build_metadata["startTime"] = current_date
@ -66,9 +74,7 @@ class CodeBuildProjectMetadata(BaseModel):
self.build_metadata["logs"] = { self.build_metadata["logs"] = {
"deepLink": "https://console.aws.amazon.com/cloudwatch/home?region=eu-west-2#logEvent:group=null;stream=null", "deepLink": "https://console.aws.amazon.com/cloudwatch/home?region=eu-west-2#logEvent:group=null;stream=null",
"cloudWatchLogsArn": "arn:aws:logs:eu-west-2:{0}:log-group:null:log-stream:null".format( "cloudWatchLogsArn": f"arn:aws:logs:{region_name}:{account_id}:log-group:null:log-stream:null",
get_account_id()
),
"cloudWatchLogs": {"status": "ENABLED"}, "cloudWatchLogs": {"status": "ENABLED"},
"s3Logs": {"status": "DISABLED", "encryptionDisabled": False}, "s3Logs": {"status": "DISABLED", "encryptionDisabled": False},
} }
@ -79,12 +85,13 @@ class CodeBuildProjectMetadata(BaseModel):
self.build_metadata["initiator"] = "rootme" self.build_metadata["initiator"] = "rootme"
self.build_metadata[ self.build_metadata[
"encryptionKey" "encryptionKey"
] = "arn:aws:kms:eu-west-2:{0}:alias/aws/s3".format(get_account_id()) ] = f"arn:aws:kms:{region_name}:{account_id}:alias/aws/s3"
class CodeBuild(BaseModel): class CodeBuild(BaseModel):
def __init__( def __init__(
self, self,
account_id,
region, region,
project_name, project_name,
project_source, project_source,
@ -97,16 +104,14 @@ class CodeBuild(BaseModel):
self.project_metadata["name"] = project_name self.project_metadata["name"] = project_name
self.project_metadata["arn"] = "arn:aws:codebuild:{0}:{1}:project/{2}".format( self.project_metadata["arn"] = "arn:aws:codebuild:{0}:{1}:project/{2}".format(
region, get_account_id(), self.project_metadata["name"] region, account_id, self.project_metadata["name"]
) )
self.project_metadata[ self.project_metadata[
"encryptionKey" "encryptionKey"
] = "arn:aws:kms:{0}:{1}:alias/aws/s3".format(region, get_account_id()) ] = f"arn:aws:kms:{region}:{account_id}:alias/aws/s3"
self.project_metadata[ self.project_metadata[
"serviceRole" "serviceRole"
] = "arn:aws:iam::{0}:role/service-role/{1}".format( ] = f"arn:aws:iam::{account_id}:role/service-role/{serviceRole}"
get_account_id(), serviceRole
)
self.project_metadata["lastModifiedDate"] = current_date self.project_metadata["lastModifiedDate"] = current_date
self.project_metadata["created"] = current_date self.project_metadata["created"] = current_date
self.project_metadata["badge"] = dict() self.project_metadata["badge"] = dict()
@ -138,6 +143,7 @@ class CodeBuildBackend(BaseBackend):
self.service_role = service_role self.service_role = service_role
self.codebuild_projects[project_name] = CodeBuild( self.codebuild_projects[project_name] = CodeBuild(
self.account_id,
self.region_name, self.region_name,
project_name, project_name,
project_source, project_source,
@ -166,7 +172,13 @@ class CodeBuildBackend(BaseBackend):
# construct a new build # construct a new build
self.build_metadata[project_name] = CodeBuildProjectMetadata( self.build_metadata[project_name] = CodeBuildProjectMetadata(
project_name, source_version, artifact_override, build_id, self.service_role self.account_id,
self.region_name,
project_name,
source_version,
artifact_override,
build_id,
self.service_role,
) )
self.build_history[project_name].append(build_id) self.build_history[project_name].append(build_id)

View File

@ -5,7 +5,6 @@ from .exceptions import (
ResourceAlreadyExistsException, ResourceAlreadyExistsException,
ResourceNotFoundException, ResourceNotFoundException,
) )
from moto.core import get_account_id
import json import json
import re import re
@ -29,11 +28,8 @@ def _validate_required_params_source(source):
raise InvalidInputException("Project source location is required") raise InvalidInputException("Project source location is required")
def _validate_required_params_service_role(service_role): def _validate_required_params_service_role(account_id, service_role):
if ( if f"arn:aws:iam::{account_id}:role/service-role/" not in service_role:
"arn:aws:iam::{0}:role/service-role/".format(get_account_id())
not in service_role
):
raise InvalidInputException( raise InvalidInputException(
"Invalid service role: Service role account ID does not match caller's account" "Invalid service role: Service role account ID does not match caller's account"
) )
@ -99,7 +95,7 @@ def _validate_required_params_id(build_id, build_ids):
class CodeBuildResponse(BaseResponse): class CodeBuildResponse(BaseResponse):
@property @property
def codebuild_backend(self): def codebuild_backend(self):
return codebuild_backends[self.region] return codebuild_backends[self.current_account][self.region]
def list_builds_for_project(self): def list_builds_for_project(self):
_validate_required_params_project_name(self._get_param("projectName")) _validate_required_params_project_name(self._get_param("projectName"))
@ -110,7 +106,7 @@ class CodeBuildResponse(BaseResponse):
): ):
raise ResourceNotFoundException( raise ResourceNotFoundException(
"The provided project arn:aws:codebuild:{0}:{1}:project/{2} does not exist".format( "The provided project arn:aws:codebuild:{0}:{1}:project/{2} does not exist".format(
self.region, get_account_id(), self._get_param("projectName") self.region, self.current_account, self._get_param("projectName")
) )
) )
@ -122,7 +118,9 @@ class CodeBuildResponse(BaseResponse):
def create_project(self): def create_project(self):
_validate_required_params_source(self._get_param("source")) _validate_required_params_source(self._get_param("source"))
_validate_required_params_service_role(self._get_param("serviceRole")) _validate_required_params_service_role(
self.current_account, self._get_param("serviceRole")
)
_validate_required_params_artifacts(self._get_param("artifacts")) _validate_required_params_artifacts(self._get_param("artifacts"))
_validate_required_params_environment(self._get_param("environment")) _validate_required_params_environment(self._get_param("environment"))
_validate_required_params_project_name(self._get_param("name")) _validate_required_params_project_name(self._get_param("name"))
@ -130,7 +128,7 @@ class CodeBuildResponse(BaseResponse):
if self._get_param("name") in self.codebuild_backend.codebuild_projects.keys(): if self._get_param("name") in self.codebuild_backend.codebuild_projects.keys():
raise ResourceAlreadyExistsException( raise ResourceAlreadyExistsException(
"Project already exists: arn:aws:codebuild:{0}:{1}:project/{2}".format( "Project already exists: arn:aws:codebuild:{0}:{1}:project/{2}".format(
self.region, get_account_id(), self._get_param("name") self.region, self.current_account, self._get_param("name")
) )
) )
@ -157,7 +155,7 @@ class CodeBuildResponse(BaseResponse):
): ):
raise ResourceNotFoundException( raise ResourceNotFoundException(
"Project cannot be found: arn:aws:codebuild:{0}:{1}:project/{2}".format( "Project cannot be found: arn:aws:codebuild:{0}:{1}:project/{2}".format(
self.region, get_account_id(), self._get_param("projectName") self.region, self.current_account, self._get_param("projectName")
) )
) )

View File

@ -1,13 +1,12 @@
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds, BackendDict from moto.core.utils import iso_8601_datetime_with_milliseconds, BackendDict
from datetime import datetime from datetime import datetime
from moto.core import get_account_id
from .exceptions import RepositoryDoesNotExistException, RepositoryNameExistsException from .exceptions import RepositoryDoesNotExistException, RepositoryNameExistsException
import uuid import uuid
class CodeCommit(BaseModel): class CodeCommit(BaseModel):
def __init__(self, region, repository_description, repository_name): def __init__(self, account_id, region, repository_description, repository_name):
current_date = iso_8601_datetime_with_milliseconds(datetime.utcnow()) current_date = iso_8601_datetime_with_milliseconds(datetime.utcnow())
self.repository_metadata = dict() self.repository_metadata = dict()
self.repository_metadata["repositoryName"] = repository_name self.repository_metadata["repositoryName"] = repository_name
@ -25,10 +24,10 @@ class CodeCommit(BaseModel):
self.repository_metadata["lastModifiedDate"] = current_date self.repository_metadata["lastModifiedDate"] = current_date
self.repository_metadata["repositoryDescription"] = repository_description self.repository_metadata["repositoryDescription"] = repository_description
self.repository_metadata["repositoryId"] = str(uuid.uuid4()) self.repository_metadata["repositoryId"] = str(uuid.uuid4())
self.repository_metadata["Arn"] = "arn:aws:codecommit:{0}:{1}:{2}".format( self.repository_metadata[
region, get_account_id(), repository_name "Arn"
) ] = f"arn:aws:codecommit:{region}:{account_id}:{repository_name}"
self.repository_metadata["accountId"] = get_account_id() self.repository_metadata["accountId"] = account_id
class CodeCommitBackend(BaseBackend): class CodeCommitBackend(BaseBackend):
@ -49,7 +48,7 @@ class CodeCommitBackend(BaseBackend):
raise RepositoryNameExistsException(repository_name) raise RepositoryNameExistsException(repository_name)
self.repositories[repository_name] = CodeCommit( self.repositories[repository_name] = CodeCommit(
self.region_name, repository_description, repository_name self.account_id, self.region_name, repository_description, repository_name
) )
return self.repositories[repository_name].repository_metadata return self.repositories[repository_name].repository_metadata

View File

@ -17,9 +17,12 @@ def _is_repository_name_valid(repository_name):
class CodeCommitResponse(BaseResponse): class CodeCommitResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="codecommit")
@property @property
def codecommit_backend(self): def codecommit_backend(self):
return codecommit_backends[self.region] return codecommit_backends[self.current_account][self.region]
def create_repository(self): def create_repository(self):
if not _is_repository_name_valid(self._get_param("repositoryName")): if not _is_repository_name_valid(self._get_param("repositoryName")):

View File

@ -14,20 +14,18 @@ from moto.codepipeline.exceptions import (
InvalidTagsException, InvalidTagsException,
TooManyTagsException, TooManyTagsException,
) )
from moto.core import get_account_id, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
class CodePipeline(BaseModel): class CodePipeline(BaseModel):
def __init__(self, region, pipeline): def __init__(self, account_id, region, pipeline):
# the version number for a new pipeline is always 1 # the version number for a new pipeline is always 1
pipeline["version"] = 1 pipeline["version"] = 1
self.pipeline = self.add_default_values(pipeline) self.pipeline = self.add_default_values(pipeline)
self.tags = {} self.tags = {}
self._arn = "arn:aws:codepipeline:{0}:{1}:{2}".format( self._arn = f"arn:aws:codepipeline:{region}:{account_id}:{pipeline['name']}"
region, get_account_id(), pipeline["name"]
)
self._created = datetime.utcnow() self._created = datetime.utcnow()
self._updated = datetime.utcnow() self._updated = datetime.utcnow()
@ -80,14 +78,13 @@ class CodePipelineBackend(BaseBackend):
@property @property
def iam_backend(self): def iam_backend(self):
return iam_backends["global"] return iam_backends[self.account_id]["global"]
def create_pipeline(self, pipeline, tags): def create_pipeline(self, pipeline, tags):
if pipeline["name"] in self.pipelines: name = pipeline["name"]
if name in self.pipelines:
raise InvalidStructureException( raise InvalidStructureException(
"A pipeline with the name '{0}' already exists in account '{1}'".format( f"A pipeline with the name '{name}' already exists in account '{self.account_id}'"
pipeline["name"], get_account_id()
)
) )
try: try:
@ -112,7 +109,9 @@ class CodePipelineBackend(BaseBackend):
"Pipeline has only 1 stage(s). There should be a minimum of 2 stages in a pipeline" "Pipeline has only 1 stage(s). There should be a minimum of 2 stages in a pipeline"
) )
self.pipelines[pipeline["name"]] = CodePipeline(self.region_name, pipeline) self.pipelines[pipeline["name"]] = CodePipeline(
self.account_id, self.region_name, pipeline
)
if tags is not None: if tags is not None:
self.pipelines[pipeline["name"]].validate_tags(tags) self.pipelines[pipeline["name"]].validate_tags(tags)
@ -129,9 +128,7 @@ class CodePipelineBackend(BaseBackend):
if not codepipeline: if not codepipeline:
raise PipelineNotFoundException( raise PipelineNotFoundException(
"Account '{0}' does not have a pipeline with name '{1}'".format( f"Account '{self.account_id}' does not have a pipeline with name '{name}'"
get_account_id(), name
)
) )
return codepipeline.pipeline, codepipeline.metadata return codepipeline.pipeline, codepipeline.metadata
@ -141,9 +138,7 @@ class CodePipelineBackend(BaseBackend):
if not codepipeline: if not codepipeline:
raise ResourceNotFoundException( raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format( f"The account with id '{self.account_id}' does not include a pipeline with the name '{pipeline['name']}'"
get_account_id(), pipeline["name"]
)
) )
# version number is auto incremented # version number is auto incremented
@ -177,9 +172,7 @@ class CodePipelineBackend(BaseBackend):
if not pipeline: if not pipeline:
raise ResourceNotFoundException( raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format( f"The account with id '{self.account_id}' does not include a pipeline with the name '{name}'"
get_account_id(), name
)
) )
tags = [{"key": key, "value": value} for key, value in pipeline.tags.items()] tags = [{"key": key, "value": value} for key, value in pipeline.tags.items()]
@ -192,9 +185,7 @@ class CodePipelineBackend(BaseBackend):
if not pipeline: if not pipeline:
raise ResourceNotFoundException( raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format( f"The account with id '{self.account_id}' does not include a pipeline with the name '{name}'"
get_account_id(), name
)
) )
pipeline.validate_tags(tags) pipeline.validate_tags(tags)
@ -208,9 +199,7 @@ class CodePipelineBackend(BaseBackend):
if not pipeline: if not pipeline:
raise ResourceNotFoundException( raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format( f"The account with id '{self.account_id}' does not include a pipeline with the name '{name}'"
get_account_id(), name
)
) )
for key in tag_keys: for key in tag_keys:

View File

@ -5,9 +5,12 @@ from .models import codepipeline_backends
class CodePipelineResponse(BaseResponse): class CodePipelineResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="codepipeline")
@property @property
def codepipeline_backend(self): def codepipeline_backend(self):
return codepipeline_backends[self.region] return codepipeline_backends[self.current_account][self.region]
def create_pipeline(self): def create_pipeline(self):
pipeline, tags = self.codepipeline_backend.create_pipeline( pipeline, tags = self.codepipeline_backend.create_pipeline(

View File

@ -4,9 +4,12 @@ from .utils import get_random_identity_id
class CognitoIdentityResponse(BaseResponse): class CognitoIdentityResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="cognito-identity")
@property @property
def backend(self): def backend(self):
return cognitoidentity_backends[self.region] return cognitoidentity_backends[self.current_account][self.region]
def create_identity_pool(self): def create_identity_pool(self):
identity_pool_name = self._get_param("IdentityPoolName") identity_pool_name = self._get_param("IdentityPoolName")
@ -64,9 +67,7 @@ class CognitoIdentityResponse(BaseResponse):
return self.backend.get_credentials_for_identity(self._get_param("IdentityId")) return self.backend.get_credentials_for_identity(self._get_param("IdentityId"))
def get_open_id_token_for_developer_identity(self): def get_open_id_token_for_developer_identity(self):
return cognitoidentity_backends[ return self.backend.get_open_id_token_for_developer_identity(
self.region
].get_open_id_token_for_developer_identity(
self._get_param("IdentityId") or get_random_identity_id(self.region) self._get_param("IdentityId") or get_random_identity_id(self.region)
) )

View File

@ -9,7 +9,6 @@ import random
from jose import jws from jose import jws
from collections import OrderedDict from collections import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core import get_account_id
from moto.core.utils import BackendDict from moto.core.utils import BackendDict
from .exceptions import ( from .exceptions import (
GroupExistsException, GroupExistsException,
@ -371,16 +370,15 @@ class CognitoIdpUserPool(BaseModel):
MAX_ID_LENGTH = 56 MAX_ID_LENGTH = 56
def __init__(self, region, name, extended_config): def __init__(self, account_id, region, name, extended_config):
self.account_id = account_id
self.region = region self.region = region
user_pool_id = generate_id( user_pool_id = generate_id(
get_cognito_idp_user_pool_id_strategy(), region, name, extended_config get_cognito_idp_user_pool_id_strategy(), region, name, extended_config
) )
self.id = "{}_{}".format(self.region, user_pool_id)[: self.MAX_ID_LENGTH] self.id = "{}_{}".format(self.region, user_pool_id)[: self.MAX_ID_LENGTH]
self.arn = "arn:aws:cognito-idp:{}:{}:userpool/{}".format( self.arn = f"arn:aws:cognito-idp:{self.region}:{account_id}:userpool/{self.id}"
self.region, get_account_id(), self.id
)
self.name = name self.name = name
self.status = None self.status = None
@ -445,7 +443,7 @@ class CognitoIdpUserPool(BaseModel):
@property @property
def backend(self): def backend(self):
return cognitoidp_backends[self.region] return cognitoidp_backends[self.account_id][self.region]
@property @property
def domain(self): def domain(self):
@ -862,7 +860,9 @@ class CognitoIdpBackend(BaseBackend):
# User pool # User pool
def create_user_pool(self, name, extended_config): def create_user_pool(self, name, extended_config):
user_pool = CognitoIdpUserPool(self.region_name, name, extended_config) user_pool = CognitoIdpUserPool(
self.account_id, self.region_name, name, extended_config
)
self.user_pools[user_pool.id] = user_pool self.user_pools[user_pool.id] = user_pool
return user_pool return user_pool
@ -1833,24 +1833,24 @@ class RegionAgnosticBackend:
# This backend will cycle through all backends as a workaround # This backend will cycle through all backends as a workaround
def _find_backend_by_access_token(self, access_token): def _find_backend_by_access_token(self, access_token):
account_specific_backends = cognitoidp_backends[get_account_id()] for account_specific_backends in cognitoidp_backends.values():
for region, backend in account_specific_backends.items(): for region, backend in account_specific_backends.items():
if region == "global": if region == "global":
continue continue
for p in backend.user_pools.values(): for p in backend.user_pools.values():
if access_token in p.access_tokens: if access_token in p.access_tokens:
return backend return backend
return account_specific_backends["us-east-1"] return backend
def _find_backend_for_clientid(self, client_id): def _find_backend_for_clientid(self, client_id):
account_specific_backends = cognitoidp_backends[get_account_id()] for account_specific_backends in cognitoidp_backends.values():
for region, backend in account_specific_backends.items(): for region, backend in account_specific_backends.items():
if region == "global": if region == "global":
continue continue
for p in backend.user_pools.values(): for p in backend.user_pools.values():
if client_id in p.clients: if client_id in p.clients:
return backend return backend
return account_specific_backends["us-east-1"] return backend
def sign_up(self, client_id, username, password, attributes): def sign_up(self, client_id, username, password, attributes):
backend = self._find_backend_for_clientid(client_id) backend = self._find_backend_for_clientid(client_id)
@ -1883,17 +1883,16 @@ cognitoidp_backends = BackendDict(CognitoIdpBackend, "cognito-idp")
# Hack to help moto-server process requests on localhost, where the region isn't # Hack to help moto-server process requests on localhost, where the region isn't
# specified in the host header. Some endpoints (change password, confirm forgot # specified in the host header. Some endpoints (change password, confirm forgot
# password) have no authorization header from which to extract the region. # password) have no authorization header from which to extract the region.
def find_region_by_value(key, value): def find_account_region_by_value(key, value):
account_specific_backends = cognitoidp_backends[get_account_id()] for account_id, account_specific_backend in cognitoidp_backends.items():
for region in account_specific_backends: for region, backend in account_specific_backend.items():
backend = cognitoidp_backends[region] for user_pool in backend.user_pools.values():
for user_pool in backend.user_pools.values(): if key == "client_id" and value in user_pool.clients:
if key == "client_id" and value in user_pool.clients: return account_id, region
return region
if key == "access_token" and value in user_pool.access_tokens: if key == "access_token" and value in user_pool.access_tokens:
return region return account_id, region
# If we can't find the `client_id` or `access_token`, we just pass # If we can't find the `client_id` or `access_token`, we just pass
# back a default backend region, which will raise the appropriate # back a default backend region, which will raise the appropriate
# error message (e.g. NotAuthorized or NotFound). # error message (e.g. NotAuthorized or NotFound).
return list(account_specific_backends)[0] return account_id, region

View File

@ -5,7 +5,7 @@ import re
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from .models import ( from .models import (
cognitoidp_backends, cognitoidp_backends,
find_region_by_value, find_account_region_by_value,
RegionAgnosticBackend, RegionAgnosticBackend,
UserStatus, UserStatus,
) )
@ -16,13 +16,16 @@ region_agnostic_backend = RegionAgnosticBackend()
class CognitoIdpResponse(BaseResponse): class CognitoIdpResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="cognito-idp")
@property @property
def parameters(self): def parameters(self):
return json.loads(self.body) return json.loads(self.body)
@property @property
def backend(self): def backend(self):
return cognitoidp_backends[self.region] return cognitoidp_backends[self.current_account][self.region]
# User pool # User pool
def create_user_pool(self): def create_user_pool(self):
@ -138,9 +141,7 @@ class CognitoIdpResponse(BaseResponse):
user_pool_id = self._get_param("UserPoolId") user_pool_id = self._get_param("UserPoolId")
max_results = self._get_param("MaxResults") max_results = self._get_param("MaxResults")
next_token = self._get_param("NextToken") next_token = self._get_param("NextToken")
user_pool_clients, next_token = cognitoidp_backends[ user_pool_clients, next_token = self.backend.list_user_pool_clients(
self.region
].list_user_pool_clients(
user_pool_id, max_results=max_results, next_token=next_token user_pool_id, max_results=max_results, next_token=next_token
) )
response = { response = {
@ -189,9 +190,7 @@ class CognitoIdpResponse(BaseResponse):
user_pool_id = self._get_param("UserPoolId") user_pool_id = self._get_param("UserPoolId")
max_results = self._get_param("MaxResults") max_results = self._get_param("MaxResults")
next_token = self._get_param("NextToken") next_token = self._get_param("NextToken")
identity_providers, next_token = cognitoidp_backends[ identity_providers, next_token = self.backend.list_identity_providers(
self.region
].list_identity_providers(
user_pool_id, max_results=max_results, next_token=next_token user_pool_id, max_results=max_results, next_token=next_token
) )
response = { response = {
@ -457,11 +456,10 @@ class CognitoIdpResponse(BaseResponse):
def forgot_password(self): def forgot_password(self):
client_id = self._get_param("ClientId") client_id = self._get_param("ClientId")
username = self._get_param("Username") username = self._get_param("Username")
region = find_region_by_value("client_id", client_id) account, region = find_account_region_by_value("client_id", client_id)
print(f"Region: {region}") confirmation_code, response = cognitoidp_backends[account][
confirmation_code, response = cognitoidp_backends[region].forgot_password( region
client_id, username ].forgot_password(client_id, username)
)
self.response_headers[ self.response_headers[
"x-moto-forgot-password-confirmation-code" "x-moto-forgot-password-confirmation-code"
] = confirmation_code ] = confirmation_code
@ -476,8 +474,8 @@ class CognitoIdpResponse(BaseResponse):
username = self._get_param("Username") username = self._get_param("Username")
password = self._get_param("Password") password = self._get_param("Password")
confirmation_code = self._get_param("ConfirmationCode") confirmation_code = self._get_param("ConfirmationCode")
region = find_region_by_value("client_id", client_id) account, region = find_account_region_by_value("client_id", client_id)
cognitoidp_backends[region].confirm_forgot_password( cognitoidp_backends[account][region].confirm_forgot_password(
client_id, username, password, confirmation_code client_id, username, password, confirmation_code
) )
return "" return ""
@ -487,8 +485,8 @@ class CognitoIdpResponse(BaseResponse):
access_token = self._get_param("AccessToken") access_token = self._get_param("AccessToken")
previous_password = self._get_param("PreviousPassword") previous_password = self._get_param("PreviousPassword")
proposed_password = self._get_param("ProposedPassword") proposed_password = self._get_param("ProposedPassword")
region = find_region_by_value("access_token", access_token) account, region = find_account_region_by_value("access_token", access_token)
cognitoidp_backends[region].change_password( cognitoidp_backends[account][region].change_password(
access_token, previous_password, proposed_password access_token, previous_password, proposed_password
) )
return "" return ""

View File

@ -50,7 +50,6 @@ from moto.config.exceptions import (
) )
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core import get_account_id
from moto.core.responses import AWSServiceSpec from moto.core.responses import AWSServiceSpec
from moto.core.utils import BackendDict from moto.core.utils import BackendDict
from moto.iam.config import role_config_query, policy_config_query from moto.iam.config import role_config_query, policy_config_query
@ -354,13 +353,13 @@ class OrganizationAggregationSource(ConfigEmptyDictable):
class ConfigAggregator(ConfigEmptyDictable): class ConfigAggregator(ConfigEmptyDictable):
def __init__(self, name, region, account_sources=None, org_source=None, tags=None): def __init__(
self, name, account_id, region, account_sources=None, org_source=None, tags=None
):
super().__init__(capitalize_start=True, capitalize_arn=False) super().__init__(capitalize_start=True, capitalize_arn=False)
self.configuration_aggregator_name = name self.configuration_aggregator_name = name
self.configuration_aggregator_arn = "arn:aws:config:{region}:{id}:config-aggregator/config-aggregator-{random}".format( self.configuration_aggregator_arn = f"arn:aws:config:{region}:{account_id}:config-aggregator/config-aggregator-{random_string()}"
region=region, id=get_account_id(), random=random_string()
)
self.account_aggregation_sources = account_sources self.account_aggregation_sources = account_sources
self.organization_aggregation_source = org_source self.organization_aggregation_source = org_source
self.creation_time = datetime2int(datetime.utcnow()) self.creation_time = datetime2int(datetime.utcnow())
@ -389,7 +388,12 @@ class ConfigAggregator(ConfigEmptyDictable):
class ConfigAggregationAuthorization(ConfigEmptyDictable): class ConfigAggregationAuthorization(ConfigEmptyDictable):
def __init__( def __init__(
self, current_region, authorized_account_id, authorized_aws_region, tags=None self,
account_id,
current_region,
authorized_account_id,
authorized_aws_region,
tags=None,
): ):
super().__init__(capitalize_start=True, capitalize_arn=False) super().__init__(capitalize_start=True, capitalize_arn=False)
@ -397,7 +401,7 @@ class ConfigAggregationAuthorization(ConfigEmptyDictable):
"arn:aws:config:{region}:{id}:aggregation-authorization/" "arn:aws:config:{region}:{id}:aggregation-authorization/"
"{auth_account}/{auth_region}".format( "{auth_account}/{auth_region}".format(
region=current_region, region=current_region,
id=get_account_id(), id=account_id,
auth_account=authorized_account_id, auth_account=authorized_account_id,
auth_region=authorized_aws_region, auth_region=authorized_aws_region,
) )
@ -413,6 +417,7 @@ class ConfigAggregationAuthorization(ConfigEmptyDictable):
class OrganizationConformancePack(ConfigEmptyDictable): class OrganizationConformancePack(ConfigEmptyDictable):
def __init__( def __init__(
self, self,
account_id,
region, region,
name, name,
delivery_s3_bucket, delivery_s3_bucket,
@ -430,11 +435,7 @@ class OrganizationConformancePack(ConfigEmptyDictable):
self.delivery_s3_key_prefix = delivery_s3_key_prefix self.delivery_s3_key_prefix = delivery_s3_key_prefix
self.excluded_accounts = excluded_accounts or [] self.excluded_accounts = excluded_accounts or []
self.last_update_time = datetime2int(datetime.utcnow()) self.last_update_time = datetime2int(datetime.utcnow())
self.organization_conformance_pack_arn = ( self.organization_conformance_pack_arn = f"arn:aws:config:{region}:{account_id}:organization-conformance-pack/{self._unique_pack_name}"
"arn:aws:config:{0}:{1}:organization-conformance-pack/{2}".format(
region, get_account_id(), self._unique_pack_name
)
)
self.organization_conformance_pack_name = name self.organization_conformance_pack_name = name
def update( def update(
@ -602,7 +603,9 @@ class Source(ConfigEmptyDictable):
OWNERS = {"AWS", "CUSTOM_LAMBDA"} OWNERS = {"AWS", "CUSTOM_LAMBDA"}
def __init__(self, region, owner, source_identifier, source_details=None): def __init__(
self, account_id, region, owner, source_identifier, source_details=None
):
super().__init__(capitalize_start=True, capitalize_arn=False) super().__init__(capitalize_start=True, capitalize_arn=False)
if owner not in Source.OWNERS: if owner not in Source.OWNERS:
raise ValidationException( raise ValidationException(
@ -644,7 +647,7 @@ class Source(ConfigEmptyDictable):
from moto.awslambda import lambda_backends from moto.awslambda import lambda_backends
try: try:
lambda_backends[region].get_function(source_identifier) lambda_backends[account_id][region].get_function(source_identifier)
except Exception: except Exception:
raise InsufficientPermissionsException( raise InsufficientPermissionsException(
f"The AWS Lambda function {source_identifier} cannot be " f"The AWS Lambda function {source_identifier} cannot be "
@ -680,8 +683,9 @@ class ConfigRule(ConfigEmptyDictable):
MAX_RULES = 150 MAX_RULES = 150
RULE_STATES = {"ACTIVE", "DELETING", "DELETING_RESULTS", "EVALUATING"} RULE_STATES = {"ACTIVE", "DELETING", "DELETING_RESULTS", "EVALUATING"}
def __init__(self, region, config_rule, tags): def __init__(self, account_id, region, config_rule, tags):
super().__init__(capitalize_start=True, capitalize_arn=False) super().__init__(capitalize_start=True, capitalize_arn=False)
self.account_id = account_id
self.config_rule_name = config_rule.get("ConfigRuleName") self.config_rule_name = config_rule.get("ConfigRuleName")
if config_rule.get("ConfigRuleArn") or config_rule.get("ConfigRuleId"): if config_rule.get("ConfigRuleArn") or config_rule.get("ConfigRuleId"):
raise InvalidParameterValueException( raise InvalidParameterValueException(
@ -694,7 +698,9 @@ class ConfigRule(ConfigEmptyDictable):
self.maximum_execution_frequency = None # keeps pylint happy self.maximum_execution_frequency = None # keeps pylint happy
self.modify_fields(region, config_rule, tags) self.modify_fields(region, config_rule, tags)
self.config_rule_id = f"config-rule-{random_string():.6}" self.config_rule_id = f"config-rule-{random_string():.6}"
self.config_rule_arn = f"arn:aws:config:{region}:{get_account_id()}:config-rule/{self.config_rule_id}" self.config_rule_arn = (
f"arn:aws:config:{region}:{account_id}:config-rule/{self.config_rule_id}"
)
def modify_fields(self, region, config_rule, tags): def modify_fields(self, region, config_rule, tags):
"""Initialize or update ConfigRule fields.""" """Initialize or update ConfigRule fields."""
@ -721,7 +727,7 @@ class ConfigRule(ConfigEmptyDictable):
self.scope = Scope(**scope_dict) self.scope = Scope(**scope_dict)
source_dict = convert_to_class_args(config_rule["Source"]) source_dict = convert_to_class_args(config_rule["Source"])
self.source = Source(region, **source_dict) self.source = Source(self.account_id, region, **source_dict)
self.input_parameters = config_rule.get("InputParameters") self.input_parameters = config_rule.get("InputParameters")
self.input_parameters_dict = {} self.input_parameters_dict = {}
@ -969,7 +975,8 @@ class ConfigBackend(BaseBackend):
): ):
aggregator = ConfigAggregator( aggregator = ConfigAggregator(
config_aggregator["ConfigurationAggregatorName"], config_aggregator["ConfigurationAggregatorName"],
self.region_name, account_id=self.account_id,
region=self.region_name,
account_sources=account_sources, account_sources=account_sources,
org_source=org_source, org_source=org_source,
tags=tags, tags=tags,
@ -1049,7 +1056,11 @@ class ConfigBackend(BaseBackend):
agg_auth = self.aggregation_authorizations.get(key) agg_auth = self.aggregation_authorizations.get(key)
if not agg_auth: if not agg_auth:
agg_auth = ConfigAggregationAuthorization( agg_auth = ConfigAggregationAuthorization(
self.region_name, authorized_account, authorized_region, tags=tags self.account_id,
self.region_name,
authorized_account,
authorized_region,
tags=tags,
) )
self.aggregation_authorizations[ self.aggregation_authorizations[
"{}/{}".format(authorized_account, authorized_region) "{}/{}".format(authorized_account, authorized_region)
@ -1345,17 +1356,22 @@ class ConfigBackend(BaseBackend):
backend_query_region = ( backend_query_region = (
backend_region # Always provide the backend this request arrived from. backend_region # Always provide the backend this request arrived from.
) )
if RESOURCE_MAP[resource_type].backends.get("global"): if RESOURCE_MAP[resource_type].backends[self.account_id].get("global"):
backend_region = "global" backend_region = "global"
# For non-aggregated queries, the we only care about the # For non-aggregated queries, the we only care about the
# backend_region. Need to verify that moto has implemented # backend_region. Need to verify that moto has implemented
# the region for the given backend: # the region for the given backend:
if RESOURCE_MAP[resource_type].backends.get(backend_region): if (
RESOURCE_MAP[resource_type]
.backends[self.account_id]
.get(backend_region)
):
# Fetch the resources for the backend's region: # Fetch the resources for the backend's region:
identifiers, new_token = RESOURCE_MAP[ identifiers, new_token = RESOURCE_MAP[
resource_type resource_type
].list_config_service_resources( ].list_config_service_resources(
self.account_id,
resource_ids, resource_ids,
resource_name, resource_name,
limit, limit,
@ -1420,6 +1436,7 @@ class ConfigBackend(BaseBackend):
identifiers, new_token = RESOURCE_MAP[ identifiers, new_token = RESOURCE_MAP[
resource_type resource_type
].list_config_service_resources( ].list_config_service_resources(
self.account_id,
resource_id, resource_id,
resource_name, resource_name,
limit, limit,
@ -1431,7 +1448,7 @@ class ConfigBackend(BaseBackend):
resource_identifiers = [] resource_identifiers = []
for identifier in identifiers: for identifier in identifiers:
item = { item = {
"SourceAccountId": get_account_id(), "SourceAccountId": self.account_id,
"SourceRegion": identifier["region"], "SourceRegion": identifier["region"],
"ResourceType": identifier["type"], "ResourceType": identifier["type"],
"ResourceId": identifier["id"], "ResourceId": identifier["id"],
@ -1468,21 +1485,25 @@ class ConfigBackend(BaseBackend):
backend_query_region = ( backend_query_region = (
backend_region # Always provide the backend this request arrived from. backend_region # Always provide the backend this request arrived from.
) )
if RESOURCE_MAP[resource_type].backends.get("global"): if RESOURCE_MAP[resource_type].backends[self.account_id].get("global"):
backend_region = "global" backend_region = "global"
# If the backend region isn't implemented then we won't find the item: # If the backend region isn't implemented then we won't find the item:
if not RESOURCE_MAP[resource_type].backends.get(backend_region): if (
not RESOURCE_MAP[resource_type]
.backends[self.account_id]
.get(backend_region)
):
raise ResourceNotDiscoveredException(resource_type, resource_id) raise ResourceNotDiscoveredException(resource_type, resource_id)
# Get the item: # Get the item:
item = RESOURCE_MAP[resource_type].get_config_resource( item = RESOURCE_MAP[resource_type].get_config_resource(
resource_id, backend_region=backend_query_region self.account_id, resource_id, backend_region=backend_query_region
) )
if not item: if not item:
raise ResourceNotDiscoveredException(resource_type, resource_id) raise ResourceNotDiscoveredException(resource_type, resource_id)
item["accountId"] = get_account_id() item["accountId"] = self.account_id
return {"configurationItems": [item]} return {"configurationItems": [item]}
@ -1512,23 +1533,31 @@ class ConfigBackend(BaseBackend):
backend_query_region = ( backend_query_region = (
backend_region # Always provide the backend this request arrived from. backend_region # Always provide the backend this request arrived from.
) )
if RESOURCE_MAP[resource["resourceType"]].backends.get("global"): if (
RESOURCE_MAP[resource["resourceType"]]
.backends[self.account_id]
.get("global")
):
config_backend_region = "global" config_backend_region = "global"
# If the backend region isn't implemented then we won't find the item: # If the backend region isn't implemented then we won't find the item:
if not RESOURCE_MAP[resource["resourceType"]].backends.get( if (
config_backend_region not RESOURCE_MAP[resource["resourceType"]]
.backends[self.account_id]
.get(config_backend_region)
): ):
continue continue
# Get the item: # Get the item:
item = RESOURCE_MAP[resource["resourceType"]].get_config_resource( item = RESOURCE_MAP[resource["resourceType"]].get_config_resource(
resource["resourceId"], backend_region=backend_query_region self.account_id,
resource["resourceId"],
backend_region=backend_query_region,
) )
if not item: if not item:
continue continue
item["accountId"] = get_account_id() item["accountId"] = self.account_id
results.append(item) results.append(item)
@ -1576,6 +1605,7 @@ class ConfigBackend(BaseBackend):
# Get the item: # Get the item:
item = RESOURCE_MAP[resource_type].get_config_resource( item = RESOURCE_MAP[resource_type].get_config_resource(
self.account_id,
resource_id, resource_id,
resource_name=resource_name, resource_name=resource_name,
resource_region=resource_region, resource_region=resource_region,
@ -1584,7 +1614,7 @@ class ConfigBackend(BaseBackend):
not_found.append(identifier) not_found.append(identifier)
continue continue
item["accountId"] = get_account_id() item["accountId"] = self.account_id
# The 'tags' field is not included in aggregate results for some reason... # The 'tags' field is not included in aggregate results for some reason...
item.pop("tags", None) item.pop("tags", None)
@ -1650,6 +1680,7 @@ class ConfigBackend(BaseBackend):
) )
else: else:
pack = OrganizationConformancePack( pack = OrganizationConformancePack(
account_id=self.account_id,
region=self.region_name, region=self.region_name,
name=name, name=name,
delivery_s3_bucket=delivery_s3_bucket, delivery_s3_bucket=delivery_s3_bucket,
@ -1723,7 +1754,7 @@ class ConfigBackend(BaseBackend):
# actually here would be a list of all accounts in the organization # actually here would be a list of all accounts in the organization
statuses = [ statuses = [
{ {
"AccountId": get_account_id(), "AccountId": self.account_id,
"ConformancePackName": "OrgConformsPack-{0}".format( "ConformancePackName": "OrgConformsPack-{0}".format(
pack._unique_pack_name pack._unique_pack_name
), ),
@ -1877,7 +1908,7 @@ class ConfigBackend(BaseBackend):
raise MaxNumberOfConfigRulesExceededException( raise MaxNumberOfConfigRulesExceededException(
rule_name, ConfigRule.MAX_RULES rule_name, ConfigRule.MAX_RULES
) )
rule = ConfigRule(self.region_name, config_rule, tags) rule = ConfigRule(self.account_id, self.region_name, config_rule, tags)
self.config_rules[rule_name] = rule self.config_rules[rule_name] = rule
return "" return ""

View File

@ -4,9 +4,12 @@ from .models import config_backends
class ConfigResponse(BaseResponse): class ConfigResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="config")
@property @property
def config_backend(self): def config_backend(self):
return config_backends[self.region] return config_backends[self.current_account][self.region]
def put_configuration_recorder(self): def put_configuration_recorder(self):
self.config_backend.put_configuration_recorder( self.config_backend.put_configuration_recorder(

View File

@ -1,4 +1,4 @@
from .models import get_account_id, ACCOUNT_ID # noqa from .models import DEFAULT_ACCOUNT_ID # noqa
from .base_backend import BaseBackend # noqa from .base_backend import BaseBackend # noqa
from .common_models import BaseModel # noqa from .common_models import BaseModel # noqa
from .common_models import CloudFormationModel, CloudWatchMetricProvider # noqa from .common_models import CloudFormationModel, CloudWatchMetricProvider # noqa

View File

@ -37,10 +37,10 @@ class CloudFormationModel(BaseModel):
@classmethod @classmethod
@abstractmethod @abstractmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
# This must be implemented as a classmethod with parameters: # This must be implemented as a classmethod with parameters:
# cls, resource_name, cloudformation_json, region_name # cls, resource_name, cloudformation_json, account_id, region_name
# Extract the resource parameters from the cloudformation json # Extract the resource parameters from the cloudformation json
# and return an instance of the resource class # and return an instance of the resource class
pass pass
@ -48,10 +48,15 @@ class CloudFormationModel(BaseModel):
@classmethod @classmethod
@abstractmethod @abstractmethod
def update_from_cloudformation_json( def update_from_cloudformation_json(
cls, original_resource, new_resource_name, cloudformation_json, region_name cls,
original_resource,
new_resource_name,
cloudformation_json,
account_id,
region_name,
): ):
# This must be implemented as a classmethod with parameters: # This must be implemented as a classmethod with parameters:
# cls, original_resource, new_resource_name, cloudformation_json, region_name # cls, original_resource, new_resource_name, cloudformation_json, account_id, region_name
# Extract the resource parameters from the cloudformation json, # Extract the resource parameters from the cloudformation json,
# delete the old resource and return the new one. Optionally inspect # delete the old resource and return the new one. Optionally inspect
# the change in parameters and no-op when nothing has changed. # the change in parameters and no-op when nothing has changed.
@ -60,10 +65,10 @@ class CloudFormationModel(BaseModel):
@classmethod @classmethod
@abstractmethod @abstractmethod
def delete_from_cloudformation_json( def delete_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name cls, resource_name, cloudformation_json, account_id, region_name
): ):
# This must be implemented as a classmethod with parameters: # This must be implemented as a classmethod with parameters:
# cls, resource_name, cloudformation_json, region_name # cls, resource_name, cloudformation_json, account_id, region_name
# Extract the resource parameters from the cloudformation json # Extract the resource parameters from the cloudformation json
# and delete the resource. Do not include a return statement. # and delete the resource. Do not include a return statement.
pass pass
@ -83,6 +88,7 @@ class ConfigQueryModel:
def list_config_service_resources( def list_config_service_resources(
self, self,
account_id,
resource_ids, resource_ids,
resource_name, resource_name,
limit, limit,
@ -114,6 +120,7 @@ class ConfigQueryModel:
As such, the proper way to implement is to first obtain a full list of results from all the region backends, and then filter As such, the proper way to implement is to first obtain a full list of results from all the region backends, and then filter
from there. It may be valuable to make this a concatenation of the region and resource name. from there. It may be valuable to make this a concatenation of the region and resource name.
:param account_id: The account number
:param resource_ids: A list of resource IDs :param resource_ids: A list of resource IDs
:param resource_name: The individual name of a resource :param resource_name: The individual name of a resource
:param limit: How many per page :param limit: How many per page
@ -140,7 +147,12 @@ class ConfigQueryModel:
raise NotImplementedError() raise NotImplementedError()
def get_config_resource( def get_config_resource(
self, resource_id, resource_name=None, backend_region=None, resource_region=None self,
account_id,
resource_id,
resource_name=None,
backend_region=None,
resource_region=None,
): ):
"""For AWS Config. This will query the backend for the specific resource type configuration. """For AWS Config. This will query the backend for the specific resource type configuration.
@ -160,6 +172,7 @@ class ConfigQueryModel:
from all resources in all regions for a given resource type*. from all resources in all regions for a given resource type*.
... ...
:param account_id:
:param resource_id: :param resource_id:
:param resource_name: :param resource_name:
:param backend_region: :param backend_region:
@ -172,5 +185,5 @@ class ConfigQueryModel:
class CloudWatchMetricProvider(object): class CloudWatchMetricProvider(object):
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def get_cloudwatch_metrics(): def get_cloudwatch_metrics(account_id):
pass pass

View File

@ -23,18 +23,7 @@ from .custom_responses_mock import (
) )
from .utils import convert_flask_to_responses_response from .utils import convert_flask_to_responses_response
ACCOUNT_ID = os.environ.get("MOTO_ACCOUNT_ID", "123456789012") DEFAULT_ACCOUNT_ID = "123456789012"
def _get_default_account_id():
return ACCOUNT_ID
account_id_resolver = _get_default_account_id
def get_account_id():
return account_id_resolver()
class BaseMockAWS: class BaseMockAWS:
@ -42,23 +31,25 @@ class BaseMockAWS:
mocks_active = False mocks_active = False
def __init__(self, backends): def __init__(self, backends):
from moto.instance_metadata import instance_metadata_backend from moto.instance_metadata import instance_metadata_backends
from moto.moto_api._internal.models import moto_api_backend from moto.moto_api._internal.models import moto_api_backend
self.backends = backends self.backends = backends
self.backends_for_urls = {} self.backends_for_urls = []
default_backends = { default_account_id = DEFAULT_ACCOUNT_ID
"instance_metadata": instance_metadata_backend, default_backends = [
"moto_api": moto_api_backend, instance_metadata_backends[default_account_id]["global"],
} moto_api_backend,
if "us-east-1" in self.backends: ]
backend_default_account = self.backends[default_account_id]
if "us-east-1" in backend_default_account:
# We only need to know the URL for a single region - they will be the same everywhere # We only need to know the URL for a single region - they will be the same everywhere
self.backends_for_urls["us-east-1"] = self.backends["us-east-1"] self.backends_for_urls.append(backend_default_account["us-east-1"])
elif "global" in self.backends: elif "global" in backend_default_account:
# If us-east-1 is not available, it's probably a global service # If us-east-1 is not available, it's probably a global service
self.backends_for_urls["global"] = self.backends["global"] self.backends_for_urls.append(backend_default_account["global"])
self.backends_for_urls.update(default_backends) self.backends_for_urls.extend(default_backends)
self.FAKE_KEYS = { self.FAKE_KEYS = {
"AWS_ACCESS_KEY_ID": "foobar_key", "AWS_ACCESS_KEY_ID": "foobar_key",
@ -283,7 +274,7 @@ class BotocoreEventMockAWS(BaseMockAWS):
def enable_patching(self, reset=True): # pylint: disable=unused-argument def enable_patching(self, reset=True): # pylint: disable=unused-argument
botocore_stubber.enabled = True botocore_stubber.enabled = True
for method in BOTOCORE_HTTP_METHODS: for method in BOTOCORE_HTTP_METHODS:
for backend in self.backends_for_urls.values(): for backend in self.backends_for_urls:
for key, value in backend.urls.items(): for key, value in backend.urls.items():
pattern = re.compile(key) pattern = re.compile(key)
botocore_stubber.register_response(method, pattern, value) botocore_stubber.register_response(method, pattern, value)
@ -295,7 +286,7 @@ class BotocoreEventMockAWS(BaseMockAWS):
for method in RESPONSES_METHODS: for method in RESPONSES_METHODS:
# for backend in default_backends.values(): # for backend in default_backends.values():
for backend in self.backends_for_urls.values(): for backend in self.backends_for_urls:
for key, value in backend.urls.items(): for key, value in backend.urls.items():
responses_mock.add( responses_mock.add(
CallbackResponse( CallbackResponse(

View File

@ -3,6 +3,7 @@ from collections import defaultdict
import datetime import datetime
import json import json
import logging import logging
import os
import re import re
import requests import requests
@ -137,7 +138,11 @@ class ActionAuthenticatorMixin(object):
>= settings.INITIAL_NO_AUTH_ACTION_COUNT >= settings.INITIAL_NO_AUTH_ACTION_COUNT
): ):
iam_request = iam_request_cls( iam_request = iam_request_cls(
method=self.method, path=self.path, data=self.data, headers=self.headers account_id=self.current_account,
method=self.method,
path=self.path,
data=self.data,
headers=self.headers,
) )
iam_request.check_signature() iam_request.check_signature()
iam_request.check_action_permitted() iam_request.check_action_permitted()
@ -215,6 +220,10 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
) )
aws_service_spec = None aws_service_spec = None
def __init__(self, service_name=None):
super().__init__()
self.service_name = service_name
@classmethod @classmethod
def dispatch(cls, *args, **kwargs): def dispatch(cls, *args, **kwargs):
return cls()._dispatch(*args, **kwargs) return cls()._dispatch(*args, **kwargs)
@ -295,6 +304,18 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
self.headers["host"] = urlparse(full_url).netloc self.headers["host"] = urlparse(full_url).netloc
self.response_headers = {"server": "amazon.com"} self.response_headers = {"server": "amazon.com"}
# Register visit with IAM
from moto.iam.models import mark_account_as_visited
self.access_key = self.get_access_key()
self.current_account = self.get_current_account()
mark_account_as_visited(
account_id=self.current_account,
access_key=self.access_key,
service=self.service_name,
region=self.region,
)
def get_region_from_url(self, request, full_url): def get_region_from_url(self, request, full_url):
url_match = self.region_regex.search(full_url) url_match = self.region_regex.search(full_url)
user_agent_match = self.region_from_useragent_regex.search( user_agent_match = self.region_from_useragent_regex.search(
@ -313,7 +334,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
region = self.default_region region = self.default_region
return region return region
def get_current_user(self): def get_access_key(self):
""" """
Returns the access key id used in this request as the current user id Returns the access key id used in this request as the current user id
""" """
@ -323,10 +344,24 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
return match.group(1) return match.group(1)
if self.querystring.get("AWSAccessKeyId"): if self.querystring.get("AWSAccessKeyId"):
return self.querystring.get("AWSAccessKeyId") return self.querystring.get("AWSAccessKeyId")[0]
else: else:
# Should we raise an unauthorized exception instead? return "AKIAEXAMPLE"
return "111122223333"
def get_current_account(self):
# PRIO 1: Check if we have a Environment Variable set
if "MOTO_ACCOUNT_ID" in os.environ:
return os.environ["MOTO_ACCOUNT_ID"]
# PRIO 2: Check if we have a specific request header that specifies the Account ID
if "x-moto-account-id" in self.headers:
return self.headers["x-moto-account-id"]
# PRIO 3: Use the access key to get the Account ID
# PRIO 4: This method will return the default Account ID as a last resort
from moto.iam.models import get_account_id_from
return get_account_id_from(self.get_access_key())
def _dispatch(self, request, full_url, headers): def _dispatch(self, request, full_url, headers):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)
@ -372,10 +407,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
# service response class should have 'SERVICE_NAME' class member, # service response class should have 'SERVICE_NAME' class member,
# if you want to get action from method and url # if you want to get action from method and url
if not hasattr(self, "SERVICE_NAME"): conn = boto3.client(self.service_name, region_name=self.region)
return None
service = self.SERVICE_NAME
conn = boto3.client(service, region_name=self.region)
# make cache if it does not exist yet # make cache if it does not exist yet
if not hasattr(self, "method_urls"): if not hasattr(self, "method_urls"):
@ -396,15 +428,15 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
def _get_action(self): def _get_action(self):
action = self.querystring.get("Action", [""])[0] action = self.querystring.get("Action", [""])[0]
if not action: # Some services use a header for the action if action:
# Headers are case-insensitive. Probably a better way to do this. return action
match = self.headers.get("x-amz-target") or self.headers.get("X-Amz-Target") # Some services use a header for the action
if match: # Headers are case-insensitive. Probably a better way to do this.
action = match.split(".")[-1] match = self.headers.get("x-amz-target") or self.headers.get("X-Amz-Target")
if match:
return match.split(".")[-1]
# get action from method and uri # get action from method and uri
if not action: return self._get_action_from_method_and_request_uri(self.method, self.path)
return self._get_action_from_method_and_request_uri(self.method, self.path)
return action
def call_action(self): def call_action(self):
headers = self.response_headers headers = self.response_headers

View File

@ -1,4 +1,4 @@
from functools import wraps from functools import lru_cache, wraps
import binascii import binascii
import datetime import datetime
@ -11,6 +11,7 @@ from boto3 import Session
from moto.settings import allow_unknown_region from moto.settings import allow_unknown_region
from threading import RLock from threading import RLock
from urllib.parse import urlparse from urllib.parse import urlparse
from uuid import uuid4
REQUEST_ID_LONG = string.digits + string.ascii_uppercase REQUEST_ID_LONG = string.digits + string.ascii_uppercase
@ -436,6 +437,20 @@ class AccountSpecificBackend(dict):
sess.get_available_regions(service_name, partition_name="aws-cn") sess.get_available_regions(service_name, partition_name="aws-cn")
) )
self.regions.extend(additional_regions or []) self.regions.extend(additional_regions or [])
self._id = str(uuid4())
def __hash__(self):
return hash(self._id)
def __eq__(self, other):
return (
other
and isinstance(other, AccountSpecificBackend)
and other._id == self._id
)
def __ne__(self, other):
return not self.__eq__(other)
def reset(self): def reset(self):
for region_specific_backend in self.values(): for region_specific_backend in self.values():
@ -444,6 +459,7 @@ class AccountSpecificBackend(dict):
def __contains__(self, region): def __contains__(self, region):
return region in self.regions or region in self.keys() return region in self.regions or region in self.keys()
@lru_cache()
def __getitem__(self, region_name): def __getitem__(self, region_name):
if region_name in self.keys(): if region_name in self.keys():
return super().__getitem__(region_name) return super().__getitem__(region_name)
@ -466,14 +482,6 @@ class BackendDict(dict):
Format: Format:
[account_id: str]: AccountSpecificBackend [account_id: str]: AccountSpecificBackend
[account_id: str][region: str] = BaseBackend [account_id: str][region: str] = BaseBackend
Full multi-account support is not yet available. We will always return account_id 123456789012, regardless of the input.
To not break existing usage patterns, the following data access pattern is also supported:
[region: str] = BaseBackend
This will automatically resolve to:
[default_account_id][region: str] = BaseBackend
""" """
def __init__( def __init__(
@ -483,46 +491,25 @@ class BackendDict(dict):
self.service_name = service_name self.service_name = service_name
self._use_boto3_regions = use_boto3_regions self._use_boto3_regions = use_boto3_regions
self._additional_regions = additional_regions self._additional_regions = additional_regions
self._id = str(uuid4())
def __contains__(self, account_id_or_region): def __hash__(self):
""" # Required for the LRUcache to work.
Possible data access patterns: # service_name is enough to determine uniqueness - other properties are dependent
backend_dict[account_id][region_name] return hash(self._id)
backend_dict[region_name]
backend_dict[unknown_region]
The latter two will be phased out in the future, and we can remove this method. def __eq__(self, other):
""" return other and isinstance(other, BackendDict) and other._id == self._id
if re.match(r"[0-9]+", account_id_or_region):
self._create_account_specific_backend("123456789012")
return True
else:
region = account_id_or_region
self._create_account_specific_backend("123456789012")
return region in self["123456789012"]
def get(self, account_id_or_region, if_none=None): def __ne__(self, other):
if self.__contains__(account_id_or_region): return not self.__eq__(other)
return self.__getitem__(account_id_or_region)
return if_none
def __getitem__(self, account_id_or_region): @lru_cache()
""" def __getitem__(self, account_id) -> AccountSpecificBackend:
Possible data access patterns: self._create_account_specific_backend(account_id)
backend_dict[account_id][region_name] return super().__getitem__(account_id)
backend_dict[region_name]
backend_dict[unknown_region]
The latter two will be phased out in the future. def _create_account_specific_backend(self, account_id) -> None:
"""
if re.match(r"[0-9]+", account_id_or_region):
self._create_account_specific_backend("123456789012")
return super().__getitem__("123456789012")
else:
region_name = account_id_or_region
return self["123456789012"][region_name]
def _create_account_specific_backend(self, account_id):
with backend_lock: with backend_lock:
if account_id not in self.keys(): if account_id not in self.keys():
self[account_id] = AccountSpecificBackend( self[account_id] = AccountSpecificBackend(

View File

@ -7,12 +7,13 @@ from .models import databrew_backends
class DataBrewResponse(BaseResponse): class DataBrewResponse(BaseResponse):
SERVICE_NAME = "databrew" def __init__(self):
super().__init__(service_name="databrew")
@property @property
def databrew_backend(self): def databrew_backend(self):
"""Return backend instance specific for this region.""" """Return backend instance specific for this region."""
return databrew_backends[self.region] return databrew_backends[self.current_account][self.region]
# region Recipes # region Recipes
@property @property

View File

@ -83,9 +83,9 @@ class Pipeline(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
datapipeline_backend = datapipeline_backends[region_name] datapipeline_backend = datapipeline_backends[account_id][region_name]
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
cloudformation_unique_id = "cf-" + resource_name cloudformation_unique_id = "cf-" + resource_name

View File

@ -5,9 +5,12 @@ from .models import datapipeline_backends
class DataPipelineResponse(BaseResponse): class DataPipelineResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="datapipeline")
@property @property
def datapipeline_backend(self): def datapipeline_backend(self):
return datapipeline_backends[self.region] return datapipeline_backends[self.current_account][self.region]
def create_pipeline(self): def create_pipeline(self):
name = self._get_param("name") name = self._get_param("name")

View File

@ -6,9 +6,12 @@ from .models import datasync_backends
class DataSyncResponse(BaseResponse): class DataSyncResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="datasync")
@property @property
def datasync_backend(self): def datasync_backend(self):
return datasync_backends[self.region] return datasync_backends[self.current_account][self.region]
def list_locations(self): def list_locations(self):
locations = list() locations = list()

View File

@ -1,5 +1,5 @@
"""DAXBackend class with methods for supported APIs.""" """DAXBackend class with methods for supported APIs."""
from moto.core import get_account_id, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict, get_random_hex, unix_time from moto.core.utils import BackendDict, get_random_hex, unix_time
from moto.moto_api import state_manager from moto.moto_api import state_manager
from moto.moto_api._internal.managed_state_model import ManagedState from moto.moto_api._internal.managed_state_model import ManagedState
@ -68,6 +68,7 @@ class DaxEndpoint:
class DaxCluster(BaseModel, ManagedState): class DaxCluster(BaseModel, ManagedState):
def __init__( def __init__(
self, self,
account_id,
region, region,
name, name,
description, description,
@ -85,7 +86,7 @@ class DaxCluster(BaseModel, ManagedState):
# Set internal properties # Set internal properties
self.name = name self.name = name
self.description = description self.description = description
self.arn = f"arn:aws:dax:{region}:{get_account_id()}:cache/{self.name}" self.arn = f"arn:aws:dax:{region}:{account_id}:cache/{self.name}"
self.node_type = node_type self.node_type = node_type
self.replication_factor = replication_factor self.replication_factor = replication_factor
self.cluster_hex = get_random_hex(6) self.cluster_hex = get_random_hex(6)
@ -187,6 +188,7 @@ class DAXBackend(BaseBackend):
AvailabilityZones, SubnetGroupNames, SecurityGroups, PreferredMaintenanceWindow, NotificationTopicArn, ParameterGroupName AvailabilityZones, SubnetGroupNames, SecurityGroups, PreferredMaintenanceWindow, NotificationTopicArn, ParameterGroupName
""" """
cluster = DaxCluster( cluster = DaxCluster(
account_id=self.account_id,
region=self.region_name, region=self.region_name,
name=cluster_name, name=cluster_name,
description=description, description=description,

View File

@ -7,9 +7,12 @@ from .models import dax_backends
class DAXResponse(BaseResponse): class DAXResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="dax")
@property @property
def dax_backend(self): def dax_backend(self):
return dax_backends[self.region] return dax_backends[self.current_account][self.region]
def create_cluster(self): def create_cluster(self):
params = json.loads(self.body) params = json.loads(self.body)

View File

@ -1,7 +1,7 @@
import json import json
from datetime import datetime from datetime import datetime
from moto.core import get_account_id, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict from moto.core.utils import BackendDict
from .exceptions import ( from .exceptions import (
@ -46,6 +46,7 @@ class DatabaseMigrationServiceBackend(BaseBackend):
migration_type=migration_type, migration_type=migration_type,
table_mappings=table_mappings, table_mappings=table_mappings,
replication_task_settings=replication_task_settings, replication_task_settings=replication_task_settings,
account_id=self.account_id,
region_name=self.region_name, region_name=self.region_name,
) )
@ -106,6 +107,7 @@ class FakeReplicationTask(BaseModel):
target_endpoint_arn, target_endpoint_arn,
table_mappings, table_mappings,
replication_task_settings, replication_task_settings,
account_id,
region_name, region_name,
): ):
self.id = replication_task_identifier self.id = replication_task_identifier
@ -117,18 +119,13 @@ class FakeReplicationTask(BaseModel):
self.table_mappings = table_mappings self.table_mappings = table_mappings
self.replication_task_settings = replication_task_settings self.replication_task_settings = replication_task_settings
self.arn = f"arn:aws:dms:{region_name}:{account_id}:task:{self.id}"
self.status = "creating" self.status = "creating"
self.creation_date = datetime.utcnow() self.creation_date = datetime.utcnow()
self.start_date = None self.start_date = None
self.stop_date = None self.stop_date = None
@property
def arn(self):
return "arn:aws:dms:{region}:{account_id}:task:{task_id}".format(
region=self.region, account_id=get_account_id(), task_id=self.id
)
def to_dict(self): def to_dict(self):
start_date = self.start_date.isoformat() if self.start_date else None start_date = self.start_date.isoformat() if self.start_date else None
stop_date = self.stop_date.isoformat() if self.stop_date else None stop_date = self.stop_date.isoformat() if self.stop_date else None

View File

@ -4,11 +4,12 @@ import json
class DatabaseMigrationServiceResponse(BaseResponse): class DatabaseMigrationServiceResponse(BaseResponse):
SERVICE_NAME = "dms" def __init__(self):
super().__init__(service_name="dms")
@property @property
def dms_backend(self): def dms_backend(self):
return dms_backends[self.region] return dms_backends[self.current_account][self.region]
def create_replication_task(self): def create_replication_task(self):
replication_task_identifier = self._get_param("ReplicationTaskIdentifier") replication_task_identifier = self._get_param("ReplicationTaskIdentifier")

View File

@ -46,6 +46,7 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
def __init__( def __init__(
self, self,
account_id,
region, region,
name, name,
password, password,
@ -57,6 +58,7 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
description=None, description=None,
edition=None, edition=None,
): # pylint: disable=too-many-arguments ): # pylint: disable=too-many-arguments
self.account_id = account_id
self.region = region self.region = region
self.name = name self.name = name
self.password = password self.password = password
@ -101,7 +103,9 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
def create_security_group(self, vpc_id): def create_security_group(self, vpc_id):
"""Create security group for the network interface.""" """Create security group for the network interface."""
security_group_info = ec2_backends[self.region].create_security_group( security_group_info = ec2_backends[self.account_id][
self.region
].create_security_group(
name=f"{self.directory_id}_controllers", name=f"{self.directory_id}_controllers",
description=( description=(
f"AWS created security group for {self.directory_id} " f"AWS created security group for {self.directory_id} "
@ -113,14 +117,18 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
def delete_security_group(self): def delete_security_group(self):
"""Delete the given security group.""" """Delete the given security group."""
ec2_backends[self.region].delete_security_group(group_id=self.security_group_id) ec2_backends[self.account_id][self.region].delete_security_group(
group_id=self.security_group_id
)
def create_eni(self, security_group_id, subnet_ids): def create_eni(self, security_group_id, subnet_ids):
"""Return ENI ids and primary addresses created for each subnet.""" """Return ENI ids and primary addresses created for each subnet."""
eni_ids = [] eni_ids = []
subnet_ips = [] subnet_ips = []
for subnet_id in subnet_ids: for subnet_id in subnet_ids:
eni_info = ec2_backends[self.region].create_network_interface( eni_info = ec2_backends[self.account_id][
self.region
].create_network_interface(
subnet=subnet_id, subnet=subnet_id,
private_ip_address=None, private_ip_address=None,
group_ids=[security_group_id], group_ids=[security_group_id],
@ -133,7 +141,7 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
def delete_eni(self): def delete_eni(self):
"""Delete ENI for each subnet and the security group.""" """Delete ENI for each subnet and the security group."""
for eni_id in self.eni_ids: for eni_id in self.eni_ids:
ec2_backends[self.region].delete_network_interface(eni_id) ec2_backends[self.account_id][self.region].delete_network_interface(eni_id)
def update_alias(self, alias): def update_alias(self, alias):
"""Change default alias to given alias.""" """Change default alias to given alias."""
@ -192,8 +200,7 @@ class DirectoryServiceBackend(BaseBackend):
service_region, zones, "ds" service_region, zones, "ds"
) )
@staticmethod def _verify_subnets(self, region, vpc_settings):
def _verify_subnets(region, vpc_settings):
"""Verify subnets are valid, else raise an exception. """Verify subnets are valid, else raise an exception.
If settings are valid, add AvailabilityZones to vpc_settings. If settings are valid, add AvailabilityZones to vpc_settings.
@ -207,7 +214,7 @@ class DirectoryServiceBackend(BaseBackend):
# Subnet IDs are checked before the VPC ID. The Subnet IDs must # Subnet IDs are checked before the VPC ID. The Subnet IDs must
# be valid and in different availability zones. # be valid and in different availability zones.
try: try:
subnets = ec2_backends[region].get_all_subnets( subnets = ec2_backends[self.account_id][region].get_all_subnets(
subnet_ids=vpc_settings["SubnetIds"] subnet_ids=vpc_settings["SubnetIds"]
) )
except InvalidSubnetIdError as exc: except InvalidSubnetIdError as exc:
@ -223,7 +230,7 @@ class DirectoryServiceBackend(BaseBackend):
"different Availability Zones." "different Availability Zones."
) )
vpcs = ec2_backends[region].describe_vpcs() vpcs = ec2_backends[self.account_id][region].describe_vpcs()
if vpc_settings["VpcId"] not in [x.id for x in vpcs]: if vpc_settings["VpcId"] not in [x.id for x in vpcs]:
raise ClientException("Invalid VPC ID.") raise ClientException("Invalid VPC ID.")
vpc_settings["AvailabilityZones"] = regions vpc_settings["AvailabilityZones"] = regions
@ -274,6 +281,7 @@ class DirectoryServiceBackend(BaseBackend):
raise DirectoryLimitExceededException("Tag Limit is exceeding") raise DirectoryLimitExceededException("Tag Limit is exceeding")
directory = Directory( directory = Directory(
self.account_id,
region, region,
name, name,
password, password,
@ -319,6 +327,7 @@ class DirectoryServiceBackend(BaseBackend):
raise DirectoryLimitExceededException("Tag Limit is exceeding") raise DirectoryLimitExceededException("Tag Limit is exceeding")
directory = Directory( directory = Directory(
self.account_id,
region, region,
name, name,
password, password,
@ -400,6 +409,7 @@ class DirectoryServiceBackend(BaseBackend):
raise DirectoryLimitExceededException("Tag Limit is exceeding") raise DirectoryLimitExceededException("Tag Limit is exceeding")
directory = Directory( directory = Directory(
self.account_id,
region, region,
name, name,
password, password,

View File

@ -10,10 +10,13 @@ from moto.ds.models import ds_backends
class DirectoryServiceResponse(BaseResponse): class DirectoryServiceResponse(BaseResponse):
"""Handler for DirectoryService requests and responses.""" """Handler for DirectoryService requests and responses."""
def __init__(self):
super().__init__(service_name="ds")
@property @property
def ds_backend(self): def ds_backend(self):
"""Return backend instance specific for this region.""" """Return backend instance specific for this region."""
return ds_backends[self.region] return ds_backends[self.current_account][self.region]
def connect_directory(self): def connect_directory(self):
"""Create an AD Connector to connect to a self-managed directory.""" """Create an AD Connector to connect to a self-managed directory."""

View File

@ -7,7 +7,6 @@ import re
import uuid import uuid
from collections import OrderedDict from collections import OrderedDict
from moto.core import get_account_id
from moto.core import BaseBackend, BaseModel, CloudFormationModel from moto.core import BaseBackend, BaseModel, CloudFormationModel
from moto.core.utils import unix_time, unix_time_millis, BackendDict from moto.core.utils import unix_time, unix_time_millis, BackendDict
from moto.core.exceptions import JsonRESTError from moto.core.exceptions import JsonRESTError
@ -252,7 +251,8 @@ class StreamRecord(BaseModel):
class StreamShard(BaseModel): class StreamShard(BaseModel):
def __init__(self, table): def __init__(self, account_id, table):
self.account_id = account_id
self.table = table self.table = table
self.id = "shardId-00000001541626099285-f35f62ef" self.id = "shardId-00000001541626099285-f35f62ef"
self.starting_sequence_number = 1100000000017454423009 self.starting_sequence_number = 1100000000017454423009
@ -285,7 +285,7 @@ class StreamShard(BaseModel):
len("arn:aws:lambda:") : arn.index(":", len("arn:aws:lambda:")) len("arn:aws:lambda:") : arn.index(":", len("arn:aws:lambda:"))
] ]
result = lambda_backends[region].send_dynamodb_items( result = lambda_backends[self.account_id][region].send_dynamodb_items(
arn, self.items, esm.event_source_arn arn, self.items, esm.event_source_arn
) )
@ -398,6 +398,7 @@ class Table(CloudFormationModel):
def __init__( def __init__(
self, self,
table_name, table_name,
account_id,
region, region,
schema=None, schema=None,
attr=None, attr=None,
@ -410,6 +411,7 @@ class Table(CloudFormationModel):
tags=None, tags=None,
): ):
self.name = table_name self.name = table_name
self.account_id = account_id
self.attr = attr self.attr = attr
self.schema = schema self.schema = schema
self.range_key_attr = None self.range_key_attr = None
@ -467,16 +469,16 @@ class Table(CloudFormationModel):
self.sse_specification = sse_specification self.sse_specification = sse_specification
if sse_specification and "KMSMasterKeyId" not in self.sse_specification: if sse_specification and "KMSMasterKeyId" not in self.sse_specification:
self.sse_specification["KMSMasterKeyId"] = self._get_default_encryption_key( self.sse_specification["KMSMasterKeyId"] = self._get_default_encryption_key(
region account_id, region
) )
def _get_default_encryption_key(self, region): def _get_default_encryption_key(self, account_id, region):
from moto.kms import kms_backends from moto.kms import kms_backends
# https://aws.amazon.com/kms/features/#AWS_Service_Integration # https://aws.amazon.com/kms/features/#AWS_Service_Integration
# An AWS managed CMK is created automatically when you first create # An AWS managed CMK is created automatically when you first create
# an encrypted resource using an AWS service integrated with KMS. # an encrypted resource using an AWS service integrated with KMS.
kms = kms_backends[region] kms = kms_backends[account_id][region]
ddb_alias = "alias/aws/dynamodb" ddb_alias = "alias/aws/dynamodb"
if not kms.alias_exists(ddb_alias): if not kms.alias_exists(ddb_alias):
key = kms.create_key( key = kms.create_key(
@ -485,7 +487,6 @@ class Table(CloudFormationModel):
key_spec="SYMMETRIC_DEFAULT", key_spec="SYMMETRIC_DEFAULT",
description="Default master key that protects my DynamoDB table storage", description="Default master key that protects my DynamoDB table storage",
tags=None, tags=None,
region=region,
) )
kms.add_alias(key.id, ddb_alias) kms.add_alias(key.id, ddb_alias)
ebs_key = kms.describe_key(ddb_alias) ebs_key = kms.describe_key(ddb_alias)
@ -532,7 +533,7 @@ class Table(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
params = {} params = {}
@ -550,27 +551,29 @@ class Table(CloudFormationModel):
if "StreamSpecification" in properties: if "StreamSpecification" in properties:
params["streams"] = properties["StreamSpecification"] params["streams"] = properties["StreamSpecification"]
table = dynamodb_backends[region_name].create_table( table = dynamodb_backends[account_id][region_name].create_table(
name=resource_name, **params name=resource_name, **params
) )
return table return table
@classmethod @classmethod
def delete_from_cloudformation_json( def delete_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name cls, resource_name, cloudformation_json, account_id, region_name
): ):
table = dynamodb_backends[region_name].delete_table(name=resource_name) table = dynamodb_backends[account_id][region_name].delete_table(
name=resource_name
)
return table return table
def _generate_arn(self, name): def _generate_arn(self, name):
return f"arn:aws:dynamodb:us-east-1:{get_account_id()}:table/{name}" return f"arn:aws:dynamodb:us-east-1:{self.account_id}:table/{name}"
def set_stream_specification(self, streams): def set_stream_specification(self, streams):
self.stream_specification = streams self.stream_specification = streams
if streams and (streams.get("StreamEnabled") or streams.get("StreamViewType")): if streams and (streams.get("StreamEnabled") or streams.get("StreamViewType")):
self.stream_specification["StreamEnabled"] = True self.stream_specification["StreamEnabled"] = True
self.latest_stream_label = datetime.datetime.utcnow().isoformat() self.latest_stream_label = datetime.datetime.utcnow().isoformat()
self.stream_shard = StreamShard(self) self.stream_shard = StreamShard(self.account_id, self)
else: else:
self.stream_specification = {"StreamEnabled": False} self.stream_specification = {"StreamEnabled": False}
@ -1042,14 +1045,14 @@ class Table(CloudFormationModel):
return results, last_evaluated_key return results, last_evaluated_key
def delete(self, region_name): def delete(self, account_id, region_name):
dynamodb_backends[region_name].delete_table(self.name) dynamodb_backends[account_id][region_name].delete_table(self.name)
class RestoredTable(Table): class RestoredTable(Table):
def __init__(self, name, region, backup): def __init__(self, name, account_id, region, backup):
params = self._parse_params_from_backup(backup) params = self._parse_params_from_backup(backup)
super().__init__(name, region=region, **params) super().__init__(name, account_id=account_id, region=region, **params)
self.indexes = copy.deepcopy(backup.table.indexes) self.indexes = copy.deepcopy(backup.table.indexes)
self.global_indexes = copy.deepcopy(backup.table.global_indexes) self.global_indexes = copy.deepcopy(backup.table.global_indexes)
self.items = copy.deepcopy(backup.table.items) self.items = copy.deepcopy(backup.table.items)
@ -1079,9 +1082,9 @@ class RestoredTable(Table):
class RestoredPITTable(Table): class RestoredPITTable(Table):
def __init__(self, name, region, source): def __init__(self, name, account_id, region, source):
params = self._parse_params_from_table(source) params = self._parse_params_from_table(source)
super().__init__(name, region=region, **params) super().__init__(name, account_id=account_id, region=region, **params)
self.indexes = copy.deepcopy(source.indexes) self.indexes = copy.deepcopy(source.indexes)
self.global_indexes = copy.deepcopy(source.global_indexes) self.global_indexes = copy.deepcopy(source.global_indexes)
self.items = copy.deepcopy(source.items) self.items = copy.deepcopy(source.items)
@ -1129,7 +1132,7 @@ class Backup(object):
def arn(self): def arn(self):
return "arn:aws:dynamodb:{region}:{account}:table/{table_name}/backup/{identifier}".format( return "arn:aws:dynamodb:{region}:{account}:table/{table_name}/backup/{identifier}".format(
region=self.backend.region_name, region=self.backend.region_name,
account=get_account_id(), account=self.backend.account_id,
table_name=self.table.name, table_name=self.table.name,
identifier=self.identifier, identifier=self.identifier,
) )
@ -1197,7 +1200,9 @@ class DynamoDBBackend(BaseBackend):
def create_table(self, name, **params): def create_table(self, name, **params):
if name in self.tables: if name in self.tables:
raise ResourceInUseException raise ResourceInUseException
table = Table(name, region=self.region_name, **params) table = Table(
name, account_id=self.account_id, region=self.region_name, **params
)
self.tables[name] = table self.tables[name] = table
return table return table
@ -1818,7 +1823,10 @@ class DynamoDBBackend(BaseBackend):
if target_table_name in self.tables: if target_table_name in self.tables:
raise TableAlreadyExistsException(target_table_name) raise TableAlreadyExistsException(target_table_name)
new_table = RestoredTable( new_table = RestoredTable(
target_table_name, region=self.region_name, backup=backup target_table_name,
account_id=self.account_id,
region=self.region_name,
backup=backup,
) )
self.tables[target_table_name] = new_table self.tables[target_table_name] = new_table
return new_table return new_table
@ -1836,7 +1844,10 @@ class DynamoDBBackend(BaseBackend):
if target_table_name in self.tables: if target_table_name in self.tables:
raise TableAlreadyExistsException(target_table_name) raise TableAlreadyExistsException(target_table_name)
new_table = RestoredPITTable( new_table = RestoredPITTable(
target_table_name, region=self.region_name, source=source target_table_name,
account_id=self.account_id,
region=self.region_name,
source=source,
) )
self.tables[target_table_name] = new_table self.tables[target_table_name] = new_table
return new_table return new_table

View File

@ -115,8 +115,6 @@ class ExpressionPathResolver(object):
raise NotImplementedError( raise NotImplementedError(
"Path resolution for {t}".format(t=type(child)) "Path resolution for {t}".format(t=type(child))
) )
if not isinstance(target, DynamoType):
print(target)
return DDBTypedValue(target) return DDBTypedValue(target)
def resolve_expression_path_nodes_to_dynamo_type( def resolve_expression_path_nodes_to_dynamo_type(

View File

@ -117,6 +117,9 @@ def check_projection_expression(expression):
class DynamoHandler(BaseResponse): class DynamoHandler(BaseResponse):
def __init__(self):
super().__init__(service_name="dynamodb")
def get_endpoint_name(self, headers): def get_endpoint_name(self, headers):
"""Parses request headers and extracts part od the X-Amz-Target """Parses request headers and extracts part od the X-Amz-Target
that corresponds to a method of DynamoHandler that corresponds to a method of DynamoHandler
@ -134,7 +137,7 @@ class DynamoHandler(BaseResponse):
:return: DynamoDB2 Backend :return: DynamoDB2 Backend
:rtype: moto.dynamodb2.models.DynamoDBBackend :rtype: moto.dynamodb2.models.DynamoDBBackend
""" """
return dynamodb_backends[self.region] return dynamodb_backends[self.current_account][self.region]
@amz_crc32 @amz_crc32
@amzn_request_id @amzn_request_id

View File

@ -5,7 +5,6 @@ import json
from collections import OrderedDict from collections import OrderedDict
from moto.core import BaseBackend, BaseModel, CloudFormationModel from moto.core import BaseBackend, BaseModel, CloudFormationModel
from moto.core.utils import unix_time, BackendDict from moto.core.utils import unix_time, BackendDict
from moto.core import get_account_id
from .comparisons import get_comparison_func from .comparisons import get_comparison_func
@ -90,6 +89,7 @@ class Item(BaseModel):
class Table(CloudFormationModel): class Table(CloudFormationModel):
def __init__( def __init__(
self, self,
account_id,
name, name,
hash_key_attr, hash_key_attr,
hash_key_type, hash_key_type,
@ -98,6 +98,7 @@ class Table(CloudFormationModel):
read_capacity=None, read_capacity=None,
write_capacity=None, write_capacity=None,
): ):
self.account_id = account_id
self.name = name self.name = name
self.hash_key_attr = hash_key_attr self.hash_key_attr = hash_key_attr
self.hash_key_type = hash_key_type self.hash_key_type = hash_key_type
@ -151,7 +152,7 @@ class Table(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
key_attr = [ key_attr = [
@ -165,6 +166,7 @@ class Table(CloudFormationModel):
if i["AttributeName"] == key_attr if i["AttributeName"] == key_attr
][0] ][0]
spec = { spec = {
"account_id": account_id,
"name": properties["TableName"], "name": properties["TableName"],
"hash_key_attr": key_attr, "hash_key_attr": key_attr,
"hash_key_type": key_type, "hash_key_type": key_type,
@ -306,9 +308,7 @@ class Table(CloudFormationModel):
if attribute_name == "StreamArn": if attribute_name == "StreamArn":
region = "us-east-1" region = "us-east-1"
time = "2000-01-01T00:00:00.000" time = "2000-01-01T00:00:00.000"
return "arn:aws:dynamodb:{0}:{1}:table/{2}/stream/{3}".format( return f"arn:aws:dynamodb:{region}:{self.account_id}:table/{self.name}/stream/{time}"
region, get_account_id(), self.name, time
)
raise UnformattedGetAttTemplateException() raise UnformattedGetAttTemplateException()
@ -318,7 +318,7 @@ class DynamoDBBackend(BaseBackend):
self.tables = OrderedDict() self.tables = OrderedDict()
def create_table(self, name, **params): def create_table(self, name, **params):
table = Table(name, **params) table = Table(self.account_id, name, **params)
self.tables[name] = table self.tables[name] = table
return table return table

View File

@ -6,6 +6,9 @@ from .models import dynamodb_backends, dynamo_json_dump
class DynamoHandler(BaseResponse): class DynamoHandler(BaseResponse):
def __init__(self):
super().__init__(service_name="dynamodb")
def get_endpoint_name(self, headers): def get_endpoint_name(self, headers):
"""Parses request headers and extracts part od the X-Amz-Target """Parses request headers and extracts part od the X-Amz-Target
that corresponds to a method of DynamoHandler that corresponds to a method of DynamoHandler
@ -38,7 +41,7 @@ class DynamoHandler(BaseResponse):
@property @property
def backend(self): def backend(self):
return dynamodb_backends["global"] return dynamodb_backends[self.current_account]["global"]
def list_tables(self): def list_tables(self):
body = self.body body = self.body

View File

@ -70,7 +70,7 @@ class DynamoDBStreamsBackend(BaseBackend):
@property @property
def dynamodb(self): def dynamodb(self):
return dynamodb_backends[self.region_name] return dynamodb_backends[self.account_id][self.region_name]
def _get_table_from_arn(self, arn): def _get_table_from_arn(self, arn):
table_name = arn.split(":", 6)[5].split("/")[1] table_name = arn.split(":", 6)[5].split("/")[1]

View File

@ -4,9 +4,12 @@ from .models import dynamodbstreams_backends
class DynamoDBStreamsHandler(BaseResponse): class DynamoDBStreamsHandler(BaseResponse):
def __init__(self):
super().__init__(service_name="dynamodb-streams")
@property @property
def backend(self): def backend(self):
return dynamodbstreams_backends[self.region] return dynamodbstreams_backends[self.current_account][self.region]
def describe_stream(self): def describe_stream(self):
arn = self._get_param("StreamArn") arn = self._get_param("StreamArn")

View File

@ -1,6 +1,6 @@
"""EBSBackend class with methods for supported APIs.""" """EBSBackend class with methods for supported APIs."""
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict, unix_time from moto.core.utils import BackendDict, unix_time
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from moto.ec2.models.elastic_block_store import Snapshot from moto.ec2.models.elastic_block_store import Snapshot
@ -17,7 +17,8 @@ class Block(BaseModel):
class EBSSnapshot(BaseModel): class EBSSnapshot(BaseModel):
def __init__(self, snapshot: Snapshot): def __init__(self, account_id, snapshot: Snapshot):
self.account_id = account_id
self.snapshot_id = snapshot.id self.snapshot_id = snapshot.id
self.status = "pending" self.status = "pending"
self.start_time = unix_time() self.start_time = unix_time()
@ -39,7 +40,7 @@ class EBSSnapshot(BaseModel):
def to_json(self): def to_json(self):
return { return {
"SnapshotId": self.snapshot_id, "SnapshotId": self.snapshot_id,
"OwnerId": ACCOUNT_ID, "OwnerId": self.account_id,
"Status": self.status, "Status": self.status,
"StartTime": self.start_time, "StartTime": self.start_time,
"VolumeSize": self.volume_size, "VolumeSize": self.volume_size,
@ -58,7 +59,7 @@ class EBSBackend(BaseBackend):
@property @property
def ec2_backend(self): def ec2_backend(self):
return ec2_backends[self.region_name] return ec2_backends[self.account_id][self.region_name]
def start_snapshot(self, volume_size, tags, description): def start_snapshot(self, volume_size, tags, description):
zone_name = f"{self.region_name}a" zone_name = f"{self.region_name}a"
@ -69,7 +70,7 @@ class EBSBackend(BaseBackend):
if tags: if tags:
tags = {tag["Key"]: tag["Value"] for tag in tags} tags = {tag["Key"]: tag["Value"] for tag in tags}
snapshot.add_tags(tags) snapshot.add_tags(tags)
ebs_snapshot = EBSSnapshot(snapshot=snapshot) ebs_snapshot = EBSSnapshot(account_id=self.account_id, snapshot=snapshot)
self.snapshots[ebs_snapshot.snapshot_id] = ebs_snapshot self.snapshots[ebs_snapshot.snapshot_id] = ebs_snapshot
return ebs_snapshot return ebs_snapshot

View File

@ -8,10 +8,13 @@ from .models import ebs_backends
class EBSResponse(BaseResponse): class EBSResponse(BaseResponse):
"""Handler for EBS requests and responses.""" """Handler for EBS requests and responses."""
def __init__(self):
super().__init__(service_name="ebs")
@property @property
def ebs_backend(self): def ebs_backend(self):
"""Return backend instance specific for this region.""" """Return backend instance specific for this region."""
return ebs_backends[self.region] return ebs_backends[self.current_account][self.region]
def snapshots(self, request, full_url, headers): def snapshots(self, request, full_url, headers):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)

View File

@ -1,5 +1,4 @@
from .models import ec2_backends from .models import ec2_backends
from ..core.models import base_decorator from ..core.models import base_decorator
ec2_backend = ec2_backends["us-east-1"]
mock_ec2 = base_decorator(ec2_backends) mock_ec2 = base_decorator(ec2_backends)

View File

@ -1,4 +1,3 @@
from moto.core import get_account_id
from moto.core import BaseBackend from moto.core import BaseBackend
from moto.core.utils import BackendDict from moto.core.utils import BackendDict
from ..exceptions import ( from ..exceptions import (
@ -55,8 +54,6 @@ from ..utils import (
get_prefix, get_prefix,
) )
OWNER_ID = get_account_id()
def validate_resource_ids(resource_ids): def validate_resource_ids(resource_ids):
if not resource_ids: if not resource_ids:
@ -72,15 +69,15 @@ class SettingsBackend:
self.ebs_encryption_by_default = False self.ebs_encryption_by_default = False
def disable_ebs_encryption_by_default(self): def disable_ebs_encryption_by_default(self):
ec2_backend = ec2_backends[self.region_name] ec2_backend = ec2_backends[self.account_id][self.region_name]
ec2_backend.ebs_encryption_by_default = False ec2_backend.ebs_encryption_by_default = False
def enable_ebs_encryption_by_default(self): def enable_ebs_encryption_by_default(self):
ec2_backend = ec2_backends[self.region_name] ec2_backend = ec2_backends[self.account_id][self.region_name]
ec2_backend.ebs_encryption_by_default = True ec2_backend.ebs_encryption_by_default = True
def get_ebs_encryption_by_default(self): def get_ebs_encryption_by_default(self):
ec2_backend = ec2_backends[self.region_name] ec2_backend = ec2_backends[self.account_id][self.region_name]
return ec2_backend.ebs_encryption_by_default return ec2_backend.ebs_encryption_by_default

View File

@ -1,7 +1,6 @@
import json import json
import re import re
from os import environ from os import environ
from moto.core import get_account_id
from moto.utilities.utils import load_resource from moto.utilities.utils import load_resource
from ..exceptions import ( from ..exceptions import (
InvalidAMIIdError, InvalidAMIIdError,
@ -34,7 +33,7 @@ class Ami(TaggedEC2Resource):
source_ami=None, source_ami=None,
name=None, name=None,
description=None, description=None,
owner_id=get_account_id(), owner_id=None,
owner_alias=None, owner_alias=None,
public=False, public=False,
virtualization_type=None, virtualization_type=None,
@ -57,7 +56,7 @@ class Ami(TaggedEC2Resource):
self.name = name self.name = name
self.image_type = image_type self.image_type = image_type
self.image_location = image_location self.image_location = image_location
self.owner_id = owner_id self.owner_id = owner_id or ec2_backend.account_id
self.owner_alias = owner_alias self.owner_alias = owner_alias
self.description = description self.description = description
self.virtualization_type = virtualization_type self.virtualization_type = virtualization_type
@ -68,9 +67,7 @@ class Ami(TaggedEC2Resource):
self.root_device_name = root_device_name self.root_device_name = root_device_name
self.root_device_type = root_device_type self.root_device_type = root_device_type
self.sriov = sriov self.sriov = sriov
self.creation_date = ( self.creation_date = creation_date or utc_date_and_time()
utc_date_and_time() if creation_date is None else creation_date
)
if instance: if instance:
self.instance = instance self.instance = instance
@ -107,7 +104,7 @@ class Ami(TaggedEC2Resource):
snapshot_description or "Auto-created snapshot for AMI %s" % self.id snapshot_description or "Auto-created snapshot for AMI %s" % self.id
) )
self.ebs_snapshot = self.ec2_backend.create_snapshot( self.ebs_snapshot = self.ec2_backend.create_snapshot(
volume.id, snapshot_description, owner_id, from_ami=ami_id volume.id, snapshot_description, self.owner_id, from_ami=ami_id
) )
self.ec2_backend.delete_volume(volume.id) self.ec2_backend.delete_volume(volume.id)
@ -185,7 +182,7 @@ class AmiBackend:
source_ami=None, source_ami=None,
name=name, name=name,
description=description, description=description,
owner_id=get_account_id(), owner_id=None,
snapshot_description=f"Created by CreateImage({instance_id}) for {ami_id}", snapshot_description=f"Created by CreateImage({instance_id}) for {ami_id}",
) )
for tag in tags: for tag in tags:
@ -196,7 +193,7 @@ class AmiBackend:
def copy_image(self, source_image_id, source_region, name=None, description=None): def copy_image(self, source_image_id, source_region, name=None, description=None):
from ..models import ec2_backends from ..models import ec2_backends
source_ami = ec2_backends[source_region].describe_images( source_ami = ec2_backends[self.account_id][source_region].describe_images(
ami_ids=[source_image_id] ami_ids=[source_image_id]
)[0] )[0]
ami_id = random_ami_id() ami_id = random_ami_id()
@ -245,7 +242,7 @@ class AmiBackend:
# support filtering by Owners=['self'] # support filtering by Owners=['self']
if "self" in owners: if "self" in owners:
owners = list( owners = list(
map(lambda o: get_account_id() if o == "self" else o, owners) map(lambda o: self.account_id if o == "self" else o, owners)
) )
images = [ images = [
ami ami

View File

@ -1,4 +1,3 @@
from moto.core import get_account_id
from moto.utilities.utils import filter_resources from moto.utilities.utils import filter_resources
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
@ -20,7 +19,7 @@ class CarrierGateway(TaggedEC2Resource):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
class CarrierGatewayBackend: class CarrierGatewayBackend:

View File

@ -1,4 +1,4 @@
from moto.core import get_account_id, CloudFormationModel from moto.core import CloudFormationModel
from moto.packages.boto.ec2.blockdevicemapping import BlockDeviceType from moto.packages.boto.ec2.blockdevicemapping import BlockDeviceType
from ..exceptions import ( from ..exceptions import (
InvalidAMIAttributeItemValueError, InvalidAMIAttributeItemValueError,
@ -38,7 +38,7 @@ class VolumeAttachment(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
@ -47,7 +47,7 @@ class VolumeAttachment(CloudFormationModel):
instance_id = properties["InstanceId"] instance_id = properties["InstanceId"]
volume_id = properties["VolumeId"] volume_id = properties["VolumeId"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
attachment = ec2_backend.attach_volume( attachment = ec2_backend.attach_volume(
volume_id=volume_id, volume_id=volume_id,
instance_id=instance_id, instance_id=instance_id,
@ -90,13 +90,13 @@ class Volume(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
volume = ec2_backend.create_volume( volume = ec2_backend.create_volume(
size=properties.get("Size"), zone_name=properties.get("AvailabilityZone") size=properties.get("Size"), zone_name=properties.get("AvailabilityZone")
) )
@ -150,7 +150,7 @@ class Snapshot(TaggedEC2Resource):
volume, volume,
description, description,
encrypted=False, encrypted=False,
owner_id=get_account_id(), owner_id=None,
from_ami=None, from_ami=None,
): ):
self.id = snapshot_id self.id = snapshot_id
@ -162,7 +162,7 @@ class Snapshot(TaggedEC2Resource):
self.ec2_backend = ec2_backend self.ec2_backend = ec2_backend
self.status = "completed" self.status = "completed"
self.encrypted = encrypted self.encrypted = encrypted
self.owner_id = owner_id self.owner_id = owner_id or ec2_backend.account_id
self.from_ami = from_ami self.from_ami = from_ami
def get_filter_value(self, filter_name): def get_filter_value(self, filter_name):
@ -339,9 +339,9 @@ class EBSBackend:
def copy_snapshot(self, source_snapshot_id, source_region, description=None): def copy_snapshot(self, source_snapshot_id, source_region, description=None):
from ..models import ec2_backends from ..models import ec2_backends
source_snapshot = ec2_backends[source_region].describe_snapshots( source_snapshot = ec2_backends[self.account_id][
snapshot_ids=[source_snapshot_id] source_region
)[0] ].describe_snapshots(snapshot_ids=[source_snapshot_id])[0]
snapshot_id = random_snapshot_id() snapshot_id = random_snapshot_id()
snapshot = Snapshot( snapshot = Snapshot(
self, self,
@ -405,7 +405,7 @@ class EBSBackend:
# an encrypted resource using an AWS service integrated with KMS. # an encrypted resource using an AWS service integrated with KMS.
from moto.kms import kms_backends from moto.kms import kms_backends
kms = kms_backends[self.region_name] kms = kms_backends[self.account_id][self.region_name]
ebs_alias = "alias/aws/ebs" ebs_alias = "alias/aws/ebs"
if not kms.alias_exists(ebs_alias): if not kms.alias_exists(ebs_alias):
key = kms.create_key( key = kms.create_key(
@ -414,7 +414,6 @@ class EBSBackend:
key_spec="SYMMETRIC_DEFAULT", key_spec="SYMMETRIC_DEFAULT",
description="Default master key that protects my EBS volumes when no other key is defined", description="Default master key that protects my EBS volumes when no other key is defined",
tags=None, tags=None,
region=self.region_name,
) )
kms.add_alias(key.id, ebs_alias) kms.add_alias(key.id, ebs_alias)
ebs_key = kms.describe_key(ebs_alias) ebs_key = kms.describe_key(ebs_alias)

View File

@ -42,11 +42,11 @@ class ElasticAddress(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
properties = cloudformation_json.get("Properties") properties = cloudformation_json.get("Properties")
instance_id = None instance_id = None

View File

@ -1,4 +1,4 @@
from moto.core import get_account_id, CloudFormationModel from moto.core import CloudFormationModel
from ..exceptions import InvalidNetworkAttachmentIdError, InvalidNetworkInterfaceIdError from ..exceptions import InvalidNetworkAttachmentIdError, InvalidNetworkInterfaceIdError
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
from .security_groups import SecurityGroup from .security_groups import SecurityGroup
@ -134,7 +134,7 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
@property @property
def association(self): def association(self):
@ -160,7 +160,7 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
@ -168,7 +168,7 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
security_group_ids = properties.get("SecurityGroups", []) security_group_ids = properties.get("SecurityGroups", [])
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
subnet_id = properties.get("SubnetId") subnet_id = properties.get("SubnetId")
if subnet_id: if subnet_id:
subnet = ec2_backend.get_subnet(subnet_id) subnet = ec2_backend.get_subnet(subnet_id)

View File

@ -58,7 +58,7 @@ class FlowLogs(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
@ -74,7 +74,7 @@ class FlowLogs(TaggedEC2Resource, CloudFormationModel):
log_format = properties.get("LogFormat") log_format = properties.get("LogFormat")
max_aggregation_interval = properties.get("MaxAggregationInterval") max_aggregation_interval = properties.get("MaxAggregationInterval")
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
flow_log, _ = ec2_backend.create_flow_logs( flow_log, _ = ec2_backend.create_flow_logs(
resource_type, resource_type,
resource_id, resource_id,
@ -219,7 +219,7 @@ class FlowLogsBackend:
arn = log_destination.split(":", 5)[5] arn = log_destination.split(":", 5)[5]
try: try:
s3_backends["global"].get_bucket(arn) s3_backends[self.account_id]["global"].get_bucket(arn)
except MissingBucket: except MissingBucket:
# Instead of creating FlowLog report # Instead of creating FlowLog report
# the unsuccessful status for the # the unsuccessful status for the
@ -242,7 +242,9 @@ class FlowLogsBackend:
try: try:
# Need something easy to check the group exists. # Need something easy to check the group exists.
# The list_tags_log_group seems to do the trick. # The list_tags_log_group seems to do the trick.
logs_backends[self.region_name].list_tags_log_group(log_group_name) logs_backends[self.account_id][
self.region_name
].list_tags_log_group(log_group_name)
except ResourceNotFoundException: except ResourceNotFoundException:
deliver_logs_status = "FAILED" deliver_logs_status = "FAILED"
deliver_logs_error_message = "Access error" deliver_logs_error_message = "Access error"

View File

@ -1,4 +1,3 @@
from moto.core import get_account_id
from moto.core import CloudFormationModel from moto.core import CloudFormationModel
from ..exceptions import ( from ..exceptions import (
IncorrectStateIamProfileAssociationError, IncorrectStateIamProfileAssociationError,
@ -10,8 +9,6 @@ from ..utils import (
filter_iam_instance_profiles, filter_iam_instance_profiles,
) )
OWNER_ID = get_account_id()
class IamInstanceProfileAssociation(CloudFormationModel): class IamInstanceProfileAssociation(CloudFormationModel):
def __init__(self, ec2_backend, association_id, instance, iam_instance_profile): def __init__(self, ec2_backend, association_id, instance, iam_instance_profile):
@ -32,7 +29,7 @@ class IamInstanceProfileAssociationBackend:
iam_association_id = random_iam_instance_profile_association_id() iam_association_id = random_iam_instance_profile_association_id()
instance_profile = filter_iam_instance_profiles( instance_profile = filter_iam_instance_profiles(
iam_instance_profile_arn, iam_instance_profile_name self.account_id, iam_instance_profile_arn, iam_instance_profile_name
) )
if instance_id in self.iam_instance_profile_associations.keys(): if instance_id in self.iam_instance_profile_associations.keys():
@ -101,7 +98,7 @@ class IamInstanceProfileAssociationBackend:
iam_instance_profile_arn=None, iam_instance_profile_arn=None,
): ):
instance_profile = filter_iam_instance_profiles( instance_profile = filter_iam_instance_profiles(
iam_instance_profile_arn, iam_instance_profile_name self.account_id, iam_instance_profile_arn, iam_instance_profile_name
) )
iam_instance_profile_association = None iam_instance_profile_association = None

View File

@ -4,7 +4,6 @@ from collections import OrderedDict
from datetime import datetime from datetime import datetime
from moto import settings from moto import settings
from moto.core import get_account_id
from moto.core import CloudFormationModel from moto.core import CloudFormationModel
from moto.core.utils import camelcase_to_underscores from moto.core.utils import camelcase_to_underscores
from moto.ec2.models.fleets import Fleet from moto.ec2.models.fleets import Fleet
@ -70,7 +69,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
super().__init__() super().__init__()
self.ec2_backend = ec2_backend self.ec2_backend = ec2_backend
self.id = random_instance_id() self.id = random_instance_id()
self.owner_id = get_account_id() self.owner_id = ec2_backend.account_id
self.lifecycle = kwargs.get("lifecycle") self.lifecycle = kwargs.get("lifecycle")
nics = kwargs.get("nics", {}) nics = kwargs.get("nics", {})
@ -265,13 +264,13 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
security_group_ids = properties.get("SecurityGroups", []) security_group_ids = properties.get("SecurityGroups", [])
group_names = [ group_names = [
ec2_backend.get_security_group_from_id(group_id).name ec2_backend.get_security_group_from_id(group_id).name
@ -307,11 +306,11 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
@classmethod @classmethod
def delete_from_cloudformation_json( def delete_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name cls, resource_name, cloudformation_json, account_id, region_name
): ):
from ..models import ec2_backends from ..models import ec2_backends
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
all_instances = ec2_backend.all_instances() all_instances = ec2_backend.all_instances()
# the resource_name for instances is the stack name, logical id, and random suffix separated # the resource_name for instances is the stack name, logical id, and random suffix separated
@ -326,7 +325,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
tag["key"] == "aws:cloudformation:logical-id" tag["key"] == "aws:cloudformation:logical-id"
and tag["value"] == logical_id and tag["value"] == logical_id
): ):
instance.delete(region_name) instance.delete(account_id, region_name)
@property @property
def physical_resource_id(self): def physical_resource_id(self):
@ -360,7 +359,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
def is_running(self): def is_running(self):
return self._state.name == "running" return self._state.name == "running"
def delete(self, region): # pylint: disable=unused-argument def delete(self, account_id, region): # pylint: disable=unused-argument
self.terminate() self.terminate()
def terminate(self): def terminate(self):

View File

@ -1,4 +1,4 @@
from moto.core import get_account_id, CloudFormationModel from moto.core import CloudFormationModel
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
from ..exceptions import ( from ..exceptions import (
@ -80,7 +80,7 @@ class InternetGateway(TaggedEC2Resource, CloudFormationModel):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
@staticmethod @staticmethod
def cloudformation_name_type(): def cloudformation_name_type():
@ -93,11 +93,11 @@ class InternetGateway(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
return ec2_backend.create_internet_gateway() return ec2_backend.create_internet_gateway()
@property @property

View File

@ -1,4 +1,3 @@
from moto.core import get_account_id
from moto.utilities.utils import filter_resources from moto.utilities.utils import filter_resources
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
from ..utils import random_managed_prefix_list_id, describe_tag_filter from ..utils import random_managed_prefix_list_id, describe_tag_filter
@ -37,9 +36,7 @@ class ManagedPrefixList(TaggedEC2Resource):
@property @property
def owner_id(self): def owner_id(self):
return ( return self.resource_owner_id or self.ec2_backend.account_id
get_account_id() if not self.resource_owner_id else self.resource_owner_id
)
class ManagedPrefixListBackend: class ManagedPrefixListBackend:

View File

@ -69,11 +69,11 @@ class NatGateway(CloudFormationModel, TaggedEC2Resource):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
nat_gateway = ec2_backend.create_nat_gateway( nat_gateway = ec2_backend.create_nat_gateway(
cloudformation_json["Properties"]["SubnetId"], cloudformation_json["Properties"]["SubnetId"],
cloudformation_json["Properties"]["AllocationId"], cloudformation_json["Properties"]["AllocationId"],

View File

@ -1,4 +1,3 @@
from moto.core import get_account_id
from ..exceptions import ( from ..exceptions import (
InvalidNetworkAclIdError, InvalidNetworkAclIdError,
InvalidRouteTableIdError, InvalidRouteTableIdError,
@ -12,9 +11,6 @@ from ..utils import (
) )
OWNER_ID = get_account_id()
class NetworkAclBackend: class NetworkAclBackend:
def __init__(self): def __init__(self):
self.network_acls = {} self.network_acls = {}
@ -210,12 +206,12 @@ class NetworkAclAssociation(object):
class NetworkAcl(TaggedEC2Resource): class NetworkAcl(TaggedEC2Resource):
def __init__( def __init__(
self, ec2_backend, network_acl_id, vpc_id, default=False, owner_id=OWNER_ID self, ec2_backend, network_acl_id, vpc_id, default=False, owner_id=None
): ):
self.ec2_backend = ec2_backend self.ec2_backend = ec2_backend
self.id = network_acl_id self.id = network_acl_id
self.vpc_id = vpc_id self.vpc_id = vpc_id
self.owner_id = owner_id self.owner_id = owner_id or ec2_backend.account_id
self.network_acl_entries = [] self.network_acl_entries = []
self.associations = {} self.associations = {}
self.default = "true" if default is True else "false" self.default = "true" if default is True else "false"

View File

@ -1,6 +1,6 @@
import ipaddress import ipaddress
from moto.core import get_account_id, CloudFormationModel from moto.core import CloudFormationModel
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
from ..exceptions import ( from ..exceptions import (
DependencyViolationError, DependencyViolationError,
@ -32,7 +32,7 @@ class RouteTable(TaggedEC2Resource, CloudFormationModel):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
@staticmethod @staticmethod
def cloudformation_name_type(): def cloudformation_name_type():
@ -45,14 +45,14 @@ class RouteTable(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
vpc_id = properties["VpcId"] vpc_id = properties["VpcId"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
route_table = ec2_backend.create_route_table(vpc_id=vpc_id) route_table = ec2_backend.create_route_table(vpc_id=vpc_id)
return route_table return route_table
@ -255,7 +255,7 @@ class Route(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
@ -270,7 +270,7 @@ class Route(CloudFormationModel):
pcx_id = properties.get("VpcPeeringConnectionId") pcx_id = properties.get("VpcPeeringConnectionId")
route_table_id = properties["RouteTableId"] route_table_id = properties["RouteTableId"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
route_table = ec2_backend.create_route( route_table = ec2_backend.create_route(
route_table_id=route_table_id, route_table_id=route_table_id,
destination_cidr_block=properties.get("DestinationCidrBlock"), destination_cidr_block=properties.get("DestinationCidrBlock"),

View File

@ -3,7 +3,7 @@ import itertools
import json import json
from collections import defaultdict from collections import defaultdict
from moto.core import get_account_id, CloudFormationModel from moto.core import CloudFormationModel
from moto.core.utils import aws_api_matches from moto.core.utils import aws_api_matches
from ..exceptions import ( from ..exceptions import (
DependencyViolationError, DependencyViolationError,
@ -30,6 +30,7 @@ from ..utils import (
class SecurityRule(object): class SecurityRule(object):
def __init__( def __init__(
self, self,
account_id,
ip_protocol, ip_protocol,
from_port, from_port,
to_port, to_port,
@ -37,6 +38,7 @@ class SecurityRule(object):
source_groups, source_groups,
prefix_list_ids=None, prefix_list_ids=None,
): ):
self.account_id = account_id
self.id = random_security_group_rule_id() self.id = random_security_group_rule_id()
self.ip_protocol = str(ip_protocol) self.ip_protocol = str(ip_protocol)
self.ip_ranges = ip_ranges or [] self.ip_ranges = ip_ranges or []
@ -69,7 +71,7 @@ class SecurityRule(object):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.account_id
def __eq__(self, other): def __eq__(self, other):
if self.ip_protocol != other.ip_protocol: if self.ip_protocol != other.ip_protocol:
@ -126,7 +128,7 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
self.egress_rules = [] self.egress_rules = []
self.enis = {} self.enis = {}
self.vpc_id = vpc_id self.vpc_id = vpc_id
self.owner_id = get_account_id() self.owner_id = ec2_backend.account_id
self.add_tags(tags or {}) self.add_tags(tags or {})
self.is_default = is_default or False self.is_default = is_default or False
@ -135,11 +137,15 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
vpc = self.ec2_backend.vpcs.get(vpc_id) vpc = self.ec2_backend.vpcs.get(vpc_id)
if vpc: if vpc:
self.egress_rules.append( self.egress_rules.append(
SecurityRule("-1", None, None, [{"CidrIp": "0.0.0.0/0"}], []) SecurityRule(
self.owner_id, "-1", None, None, [{"CidrIp": "0.0.0.0/0"}], []
)
) )
if vpc and len(vpc.get_cidr_block_association_set(ipv6=True)) > 0: if vpc and len(vpc.get_cidr_block_association_set(ipv6=True)) > 0:
self.egress_rules.append( self.egress_rules.append(
SecurityRule("-1", None, None, [{"CidrIpv6": "::/0"}], []) SecurityRule(
self.owner_id, "-1", None, None, [{"CidrIpv6": "::/0"}], []
)
) )
# each filter as a simple function in a mapping # each filter as a simple function in a mapping
@ -181,13 +187,13 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
vpc_id = properties.get("VpcId") vpc_id = properties.get("VpcId")
security_group = ec2_backend.create_security_group( security_group = ec2_backend.create_security_group(
name=resource_name, name=resource_name,
@ -223,35 +229,44 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def update_from_cloudformation_json( def update_from_cloudformation_json(
cls, original_resource, new_resource_name, cloudformation_json, region_name cls,
original_resource,
new_resource_name,
cloudformation_json,
account_id,
region_name,
): ):
cls._delete_security_group_given_vpc_id( cls._delete_security_group_given_vpc_id(
original_resource.name, original_resource.vpc_id, region_name original_resource.name, original_resource.vpc_id, account_id, region_name
) )
return cls.create_from_cloudformation_json( return cls.create_from_cloudformation_json(
new_resource_name, cloudformation_json, region_name new_resource_name, cloudformation_json, account_id, region_name
) )
@classmethod @classmethod
def delete_from_cloudformation_json( def delete_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name cls, resource_name, cloudformation_json, account_id, region_name
): ):
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
vpc_id = properties.get("VpcId") vpc_id = properties.get("VpcId")
cls._delete_security_group_given_vpc_id(resource_name, vpc_id, region_name) cls._delete_security_group_given_vpc_id(
resource_name, vpc_id, account_id, region_name
)
@classmethod @classmethod
def _delete_security_group_given_vpc_id(cls, resource_name, vpc_id, region_name): def _delete_security_group_given_vpc_id(
cls, resource_name, vpc_id, account_id, region_name
):
from ..models import ec2_backends from ..models import ec2_backends
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
security_group = ec2_backend.get_security_group_by_name_or_id( security_group = ec2_backend.get_security_group_by_name_or_id(
resource_name, vpc_id resource_name, vpc_id
) )
if security_group: if security_group:
security_group.delete(region_name) security_group.delete(account_id, region_name)
def delete(self, region_name): # pylint: disable=unused-argument def delete(self, account_id, region_name): # pylint: disable=unused-argument
"""Not exposed as part of the ELB API - used for CloudFormation.""" """Not exposed as part of the ELB API - used for CloudFormation."""
self.ec2_backend.delete_security_group(group_id=self.id) self.ec2_backend.delete_security_group(group_id=self.id)
@ -603,7 +618,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id) _source_groups = self._add_source_group(source_groups, vpc_id)
security_rule = SecurityRule( security_rule = SecurityRule(
ip_protocol, from_port, to_port, ip_ranges, _source_groups, prefix_list_ids self.account_id,
ip_protocol,
from_port,
to_port,
ip_ranges,
_source_groups,
prefix_list_ids,
) )
if security_rule in group.ingress_rules: if security_rule in group.ingress_rules:
@ -660,7 +681,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id) _source_groups = self._add_source_group(source_groups, vpc_id)
security_rule = SecurityRule( security_rule = SecurityRule(
ip_protocol, from_port, to_port, ip_ranges, _source_groups, prefix_list_ids self.account_id,
ip_protocol,
from_port,
to_port,
ip_ranges,
_source_groups,
prefix_list_ids,
) )
# To match drift property of the security rules. # To match drift property of the security rules.
@ -748,7 +775,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id) _source_groups = self._add_source_group(source_groups, vpc_id)
security_rule = SecurityRule( security_rule = SecurityRule(
ip_protocol, from_port, to_port, ip_ranges, _source_groups, prefix_list_ids self.account_id,
ip_protocol,
from_port,
to_port,
ip_ranges,
_source_groups,
prefix_list_ids,
) )
if security_rule in group.egress_rules: if security_rule in group.egress_rules:
@ -820,7 +853,13 @@ class SecurityGroupBackend:
ip_ranges.remove(item) ip_ranges.remove(item)
security_rule = SecurityRule( security_rule = SecurityRule(
ip_protocol, from_port, to_port, ip_ranges, _source_groups, prefix_list_ids self.account_id,
ip_protocol,
from_port,
to_port,
ip_ranges,
_source_groups,
prefix_list_ids,
) )
# To match drift property of the security rules. # To match drift property of the security rules.
@ -901,7 +940,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id) _source_groups = self._add_source_group(source_groups, vpc_id)
security_rule = SecurityRule( security_rule = SecurityRule(
ip_protocol, from_port, to_port, ip_ranges, _source_groups, prefix_list_ids self.account_id,
ip_protocol,
from_port,
to_port,
ip_ranges,
_source_groups,
prefix_list_ids,
) )
for rule in group.ingress_rules: for rule in group.ingress_rules:
if ( if (
@ -951,7 +996,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id) _source_groups = self._add_source_group(source_groups, vpc_id)
security_rule = SecurityRule( security_rule = SecurityRule(
ip_protocol, from_port, to_port, ip_ranges, _source_groups, prefix_list_ids self.account_id,
ip_protocol,
from_port,
to_port,
ip_ranges,
_source_groups,
prefix_list_ids,
) )
for rule in group.egress_rules: for rule in group.egress_rules:
if ( if (
@ -1008,7 +1059,7 @@ class SecurityGroupBackend:
_source_groups = [] _source_groups = []
for item in source_groups or []: for item in source_groups or []:
if "OwnerId" not in item: if "OwnerId" not in item:
item["OwnerId"] = get_account_id() item["OwnerId"] = self.account_id
# for VPCs # for VPCs
if "GroupId" in item: if "GroupId" in item:
if not self.get_security_group_by_name_or_id( if not self.get_security_group_by_name_or_id(
@ -1061,13 +1112,13 @@ class SecurityGroupIngress(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
group_name = properties.get("GroupName") group_name = properties.get("GroupName")
group_id = properties.get("GroupId") group_id = properties.get("GroupId")
ip_protocol = properties.get("IpProtocol") ip_protocol = properties.get("IpProtocol")

View File

@ -306,12 +306,12 @@ class SpotFleetRequest(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"]["SpotFleetRequestConfigData"] properties = cloudformation_json["Properties"]["SpotFleetRequestConfigData"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
spot_price = properties.get("SpotPrice") spot_price = properties.get("SpotPrice")
target_capacity = properties["TargetCapacity"] target_capacity = properties["TargetCapacity"]

View File

@ -2,7 +2,6 @@ import ipaddress
import itertools import itertools
from collections import defaultdict from collections import defaultdict
from moto.core import get_account_id
from moto.core import CloudFormationModel from moto.core import CloudFormationModel
from ..exceptions import ( from ..exceptions import (
GenericInvalidParameterValueError, GenericInvalidParameterValueError,
@ -64,7 +63,7 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
@staticmethod @staticmethod
def cloudformation_name_type(): def cloudformation_name_type():
@ -77,7 +76,7 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
@ -86,7 +85,7 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
vpc_id = properties["VpcId"] vpc_id = properties["VpcId"]
cidr_block = properties["CidrBlock"] cidr_block = properties["CidrBlock"]
availability_zone = properties.get("AvailabilityZone") availability_zone = properties.get("AvailabilityZone")
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
subnet = ec2_backend.create_subnet( subnet = ec2_backend.create_subnet(
vpc_id=vpc_id, cidr_block=cidr_block, availability_zone=availability_zone vpc_id=vpc_id, cidr_block=cidr_block, availability_zone=availability_zone
) )
@ -415,7 +414,7 @@ class SubnetRouteTableAssociation(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
@ -424,7 +423,7 @@ class SubnetRouteTableAssociation(CloudFormationModel):
route_table_id = properties["RouteTableId"] route_table_id = properties["RouteTableId"]
subnet_id = properties["SubnetId"] subnet_id = properties["SubnetId"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
subnet_association = ec2_backend.create_subnet_association( subnet_association = ec2_backend.create_subnet_association(
route_table_id=route_table_id, subnet_id=subnet_id route_table_id=route_table_id, subnet_id=subnet_id
) )

View File

@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from moto.core import get_account_id, CloudFormationModel from moto.core import CloudFormationModel
from moto.core.utils import iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.utilities.utils import filter_resources, merge_multiple_dicts from moto.utilities.utils import filter_resources, merge_multiple_dicts
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
@ -38,7 +38,7 @@ class TransitGateway(TaggedEC2Resource, CloudFormationModel):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
@staticmethod @staticmethod
def cloudformation_name_type(): def cloudformation_name_type():
@ -51,11 +51,11 @@ class TransitGateway(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
description = properties["Description"] description = properties["Description"]
options = dict(properties) options = dict(properties)

View File

@ -1,5 +1,4 @@
from datetime import datetime from datetime import datetime
from moto.core import get_account_id
from moto.core.utils import iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.utilities.utils import merge_multiple_dicts, filter_resources from moto.utilities.utils import merge_multiple_dicts, filter_resources
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
@ -24,20 +23,14 @@ class TransitGatewayAttachment(TaggedEC2Resource):
self.add_tags(tags or {}) self.add_tags(tags or {})
self._created_at = datetime.utcnow() self._created_at = datetime.utcnow()
self.owner_id = self.resource_owner_id self.resource_owner_id = backend.account_id
self.transit_gateway_owner_id = backend.account_id
self.owner_id = backend.account_id
@property @property
def create_time(self): def create_time(self):
return iso_8601_datetime_with_milliseconds(self._created_at) return iso_8601_datetime_with_milliseconds(self._created_at)
@property
def resource_owner_id(self):
return get_account_id()
@property
def transit_gateway_owner_id(self):
return get_account_id()
class TransitGatewayVpcAttachment(TransitGatewayAttachment): class TransitGatewayVpcAttachment(TransitGatewayAttachment):
DEFAULT_OPTIONS = { DEFAULT_OPTIONS = {
@ -93,10 +86,6 @@ class TransitGatewayPeeringAttachment(TransitGatewayAttachment):
} }
self.status = PeeringConnectionStatus() self.status = PeeringConnectionStatus()
@property
def resource_owner_id(self):
return get_account_id()
class TransitGatewayAttachmentBackend: class TransitGatewayAttachmentBackend:
def __init__(self): def __init__(self):

View File

@ -65,13 +65,13 @@ class VPCPeeringConnection(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
vpc = ec2_backend.get_vpc(properties["VpcId"]) vpc = ec2_backend.get_vpc(properties["VpcId"])
peer_vpc = ec2_backend.get_vpc(properties["PeerVpcId"]) peer_vpc = ec2_backend.get_vpc(properties["PeerVpcId"])

View File

@ -44,7 +44,7 @@ class VPCServiceConfigurationBackend:
def elbv2_backend(self): def elbv2_backend(self):
from moto.elbv2.models import elbv2_backends from moto.elbv2.models import elbv2_backends
return elbv2_backends[self.region_name] return elbv2_backends[self.account_id][self.region_name]
def get_vpc_endpoint_service(self, resource_id): def get_vpc_endpoint_service(self, resource_id):
return self.configurations.get(resource_id) return self.configurations.get(resource_id)

View File

@ -4,7 +4,6 @@ import weakref
from collections import defaultdict from collections import defaultdict
from operator import itemgetter from operator import itemgetter
from moto.core import get_account_id
from moto.core import CloudFormationModel from moto.core import CloudFormationModel
from .core import TaggedEC2Resource from .core import TaggedEC2Resource
from ..exceptions import ( from ..exceptions import (
@ -85,7 +84,7 @@ class VPCEndPoint(TaggedEC2Resource, CloudFormationModel):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
@property @property
def physical_resource_id(self): def physical_resource_id(self):
@ -101,7 +100,7 @@ class VPCEndPoint(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
@ -116,7 +115,7 @@ class VPCEndPoint(TaggedEC2Resource, CloudFormationModel):
route_table_ids = properties.get("RouteTableIds") route_table_ids = properties.get("RouteTableIds")
security_group_ids = properties.get("SecurityGroupIds") security_group_ids = properties.get("SecurityGroupIds")
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
vpc_endpoint = ec2_backend.create_vpc_endpoint( vpc_endpoint = ec2_backend.create_vpc_endpoint(
vpc_id=vpc_id, vpc_id=vpc_id,
service_name=service_name, service_name=service_name,
@ -167,7 +166,7 @@ class VPC(TaggedEC2Resource, CloudFormationModel):
@property @property
def owner_id(self): def owner_id(self):
return get_account_id() return self.ec2_backend.account_id
@staticmethod @staticmethod
def cloudformation_name_type(): def cloudformation_name_type():
@ -180,13 +179,13 @@ class VPC(TaggedEC2Resource, CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
vpc = ec2_backend.create_vpc( vpc = ec2_backend.create_vpc(
cidr_block=properties["CidrBlock"], cidr_block=properties["CidrBlock"],
instance_tenancy=properties.get("InstanceTenancy", "default"), instance_tenancy=properties.get("InstanceTenancy", "default"),
@ -629,7 +628,7 @@ class VPCBackend:
return generic_filter(filters, vpc_end_points) return generic_filter(filters, vpc_end_points)
@staticmethod @staticmethod
def _collect_default_endpoint_services(region): def _collect_default_endpoint_services(account_id, region):
"""Return list of default services using list of backends.""" """Return list of default services using list of backends."""
if DEFAULT_VPC_ENDPOINT_SERVICES: if DEFAULT_VPC_ENDPOINT_SERVICES:
return DEFAULT_VPC_ENDPOINT_SERVICES return DEFAULT_VPC_ENDPOINT_SERVICES
@ -643,7 +642,8 @@ class VPCBackend:
from moto import backends # pylint: disable=import-outside-toplevel from moto import backends # pylint: disable=import-outside-toplevel
for _backends in backends.unique_backends(): for _backends in backends.service_backends():
_backends = _backends[account_id]
if region in _backends: if region in _backends:
service = _backends[region].default_vpc_endpoint_service(region, zones) service = _backends[region].default_vpc_endpoint_service(region, zones)
if service: if service:
@ -757,7 +757,9 @@ class VPCBackend:
The DryRun parameter is ignored. The DryRun parameter is ignored.
""" """
default_services = self._collect_default_endpoint_services(region) default_services = self._collect_default_endpoint_services(
self.account_id, region
)
for service_name in service_names: for service_name in service_names:
if service_name not in [x["ServiceName"] for x in default_services]: if service_name not in [x["ServiceName"] for x in default_services]:
raise InvalidServiceName(service_name) raise InvalidServiceName(service_name)

View File

@ -22,13 +22,13 @@ class VPCGatewayAttachment(CloudFormationModel):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
vpn_gateway_id = properties.get("VpnGatewayId", None) vpn_gateway_id = properties.get("VpnGatewayId", None)
internet_gateway_id = properties.get("InternetGatewayId", None) internet_gateway_id = properties.get("InternetGatewayId", None)
if vpn_gateway_id: if vpn_gateway_id:
@ -78,14 +78,14 @@ class VpnGateway(CloudFormationModel, TaggedEC2Resource):
@classmethod @classmethod
def create_from_cloudformation_json( def create_from_cloudformation_json(
cls, resource_name, cloudformation_json, region_name, **kwargs cls, resource_name, cloudformation_json, account_id, region_name, **kwargs
): ):
from ..models import ec2_backends from ..models import ec2_backends
properties = cloudformation_json["Properties"] properties = cloudformation_json["Properties"]
_type = properties["Type"] _type = properties["Type"]
asn = properties.get("AmazonSideAsn", None) asn = properties.get("AmazonSideAsn", None)
ec2_backend = ec2_backends[region_name] ec2_backend = ec2_backends[account_id][region_name]
return ec2_backend.create_vpn_gateway(gateway_type=_type, amazon_side_asn=asn) return ec2_backend.create_vpn_gateway(gateway_type=_type, amazon_side_asn=asn)

View File

@ -88,11 +88,14 @@ class EC2Response(
IamInstanceProfiles, IamInstanceProfiles,
CarrierGateway, CarrierGateway,
): ):
def __init__(self):
super().__init__(service_name="ec2")
@property @property
def ec2_backend(self): def ec2_backend(self):
from moto.ec2.models import ec2_backends from moto.ec2.models import ec2_backends
return ec2_backends[self.region] return ec2_backends[self.current_account][self.region]
@property @property
def should_autoescape(self): def should_autoescape(self):

View File

@ -4,7 +4,6 @@ from moto.ec2.exceptions import (
InvalidParameterCombination, InvalidParameterCombination,
InvalidRequest, InvalidRequest,
) )
from moto.core import get_account_id
from copy import deepcopy from copy import deepcopy
@ -36,7 +35,11 @@ class InstanceResponse(EC2BaseResponse):
next_token = reservations_resp[-1].id next_token = reservations_resp[-1].id
template = self.response_template(EC2_DESCRIBE_INSTANCES) template = self.response_template(EC2_DESCRIBE_INSTANCES)
return ( return (
template.render(reservations=reservations_resp, next_token=next_token) template.render(
account_id=self.current_account,
reservations=reservations_resp,
next_token=next_token,
)
.replace("True", "true") .replace("True", "true")
.replace("False", "false") .replace("False", "false")
) )
@ -85,7 +88,9 @@ class InstanceResponse(EC2BaseResponse):
) )
template = self.response_template(EC2_RUN_INSTANCES) template = self.response_template(EC2_RUN_INSTANCES)
return template.render(reservation=new_reservation) return template.render(
account_id=self.current_account, reservation=new_reservation
)
def terminate_instances(self): def terminate_instances(self):
instance_ids = self._get_multi_param("InstanceId") instance_ids = self._get_multi_param("InstanceId")
@ -94,8 +99,12 @@ class InstanceResponse(EC2BaseResponse):
from moto.autoscaling import autoscaling_backends from moto.autoscaling import autoscaling_backends
from moto.elbv2 import elbv2_backends from moto.elbv2 import elbv2_backends
autoscaling_backends[self.region].notify_terminate_instances(instance_ids) autoscaling_backends[self.current_account][
elbv2_backends[self.region].notify_terminate_instances(instance_ids) self.region
].notify_terminate_instances(instance_ids)
elbv2_backends[self.current_account][
self.region
].notify_terminate_instances(instance_ids)
template = self.response_template(EC2_TERMINATE_INSTANCES) template = self.response_template(EC2_TERMINATE_INSTANCES)
return template.render(instances=instances) return template.render(instances=instances)
@ -380,13 +389,10 @@ BLOCK_DEVICE_MAPPING_TEMPLATE = {
}, },
} }
EC2_RUN_INSTANCES = ( EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
"""<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<reservationId>{{ reservation.id }}</reservationId> <reservationId>{{ reservation.id }}</reservationId>
<ownerId>""" <ownerId>{{ account_id }}</ownerId>
+ get_account_id()
+ """</ownerId>
<groupSet> <groupSet>
<item> <item>
<groupId>sg-245f6a01</groupId> <groupId>sg-245f6a01</groupId>
@ -473,9 +479,7 @@ EC2_RUN_INSTANCES = (
<vpcId>{{ nic.subnet.vpc_id }}</vpcId> <vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% endif %} {% endif %}
<description>Primary network interface</description> <description>Primary network interface</description>
<ownerId>""" <ownerId>{{ account_id }}</ownerId>
+ get_account_id()
+ """</ownerId>
<status>in-use</status> <status>in-use</status>
<macAddress>1b:2b:3c:4d:5e:6f</macAddress> <macAddress>1b:2b:3c:4d:5e:6f</macAddress>
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress> <privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
@ -498,9 +502,7 @@ EC2_RUN_INSTANCES = (
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>""" <ipOwnerId>{{ account_id }}</ipOwnerId>
+ get_account_id()
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
<privateIpAddressesSet> <privateIpAddressesSet>
@ -510,9 +512,7 @@ EC2_RUN_INSTANCES = (
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>""" <ipOwnerId>{{ account_id }}</ipOwnerId>
+ get_account_id()
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
</item> </item>
@ -524,18 +524,14 @@ EC2_RUN_INSTANCES = (
{% endfor %} {% endfor %}
</instancesSet> </instancesSet>
</RunInstancesResponse>""" </RunInstancesResponse>"""
)
EC2_DESCRIBE_INSTANCES = ( EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
"""<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>fdcdcab1-ae5c-489e-9c33-4637c5dda355</requestId> <requestId>fdcdcab1-ae5c-489e-9c33-4637c5dda355</requestId>
<reservationSet> <reservationSet>
{% for reservation in reservations %} {% for reservation in reservations %}
<item> <item>
<reservationId>{{ reservation.id }}</reservationId> <reservationId>{{ reservation.id }}</reservationId>
<ownerId>""" <ownerId>{{ account_id }}</ownerId>
+ get_account_id()
+ """</ownerId>
<groupSet> <groupSet>
{% for group in reservation.dynamic_group_list %} {% for group in reservation.dynamic_group_list %}
<item> <item>
@ -633,9 +629,7 @@ EC2_DESCRIBE_INSTANCES = (
{% endfor %} {% endfor %}
</blockDeviceMapping> </blockDeviceMapping>
<virtualizationType>{{ instance.virtualization_type }}</virtualizationType> <virtualizationType>{{ instance.virtualization_type }}</virtualizationType>
<clientToken>ABCDE""" <clientToken>ABCDE{{ account_id }}3</clientToken>
+ get_account_id()
+ """3</clientToken>
{% if instance.get_tags() %} {% if instance.get_tags() %}
<tagSet> <tagSet>
{% for tag in instance.get_tags() %} {% for tag in instance.get_tags() %}
@ -658,9 +652,7 @@ EC2_DESCRIBE_INSTANCES = (
<vpcId>{{ nic.subnet.vpc_id }}</vpcId> <vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% endif %} {% endif %}
<description>Primary network interface</description> <description>Primary network interface</description>
<ownerId>""" <ownerId>{{ account_id }}</ownerId>
+ get_account_id()
+ """</ownerId>
<status>in-use</status> <status>in-use</status>
<macAddress>1b:2b:3c:4d:5e:6f</macAddress> <macAddress>1b:2b:3c:4d:5e:6f</macAddress>
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress> <privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
@ -687,9 +679,7 @@ EC2_DESCRIBE_INSTANCES = (
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>""" <ipOwnerId>{{ account_id }}</ipOwnerId>
+ get_account_id()
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
<privateIpAddressesSet> <privateIpAddressesSet>
@ -699,9 +689,7 @@ EC2_DESCRIBE_INSTANCES = (
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>""" <ipOwnerId>{{ account_id }}</ipOwnerId>
+ get_account_id()
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
</item> </item>
@ -719,7 +707,6 @@ EC2_DESCRIBE_INSTANCES = (
<nextToken>{{ next_token }}</nextToken> <nextToken>{{ next_token }}</nextToken>
{% endif %} {% endif %}
</DescribeInstancesResponse>""" </DescribeInstancesResponse>"""
)
EC2_TERMINATE_INSTANCES = """ EC2_TERMINATE_INSTANCES = """
<TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> <TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">

Some files were not shown because too many files have changed in this diff Show More