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_tags_for_resource
- [X] start_application
- [ ] start_job_run
- [X] start_job_run
- [X] stop_application
- [ ] tag_resource
- [ ] untag_resource

View File

@ -1,7 +1,7 @@
codecov:
notify:
# Leave a GitHub comment after all builds have passed
after_n_builds: 8
after_n_builds: 10
coverage:
status:
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/iam
docs/aws_config
docs/multi_account
.. toctree::
:hidden:

View File

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

View File

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

View File

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

View File

@ -13,6 +13,9 @@ ENDPOINT_CONFIGURATION_TYPES = ["PRIVATE", "EDGE", "REGIONAL"]
class APIGatewayResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="apigateway")
def error(self, type_, message, status=400):
headers = self.response_headers or {}
headers["X-Amzn-Errortype"] = type_
@ -20,7 +23,7 @@ class APIGatewayResponse(BaseResponse):
@property
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):
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):
"""Handler for ApiGatewayV2 requests and responses."""
def __init__(self):
super().__init__(service_name="apigatewayv2")
@property
def apigatewayv2_backend(self):
"""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):
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.ecs import ecs_backends
from .exceptions import AWSValidationException
@ -65,7 +65,7 @@ class ScalableDimensionValueSet(Enum):
class ApplicationAutoscalingBackend(BaseBackend):
def __init__(self, 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.policies = {}
self.scheduled_actions = list()
@ -77,10 +77,6 @@ class ApplicationAutoscalingBackend(BaseBackend):
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):
"""Describe scalable targets."""
if r_ids is None:
@ -305,6 +301,7 @@ class ApplicationAutoscalingBackend(BaseBackend):
start_time,
end_time,
scalable_target_action,
self.account_id,
self.region_name,
)
self.scheduled_actions.append(action)
@ -450,9 +447,10 @@ class FakeScheduledAction(BaseModel):
start_time,
end_time,
scalable_target_action,
account_id,
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.schedule = schedule
self.timezone = timezone

View File

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

View File

@ -1,6 +1,6 @@
import base64
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.utilities.tagging_service import TaggingService
@ -53,6 +53,7 @@ class GraphqlSchema(BaseModel):
class GraphqlAPI(BaseModel):
def __init__(
self,
account_id,
region,
name,
authentication_type,
@ -74,9 +75,7 @@ class GraphqlAPI(BaseModel):
self.user_pool_config = user_pool_config
self.xray_enabled = xray_enabled
self.arn = (
f"arn:aws:appsync:{self.region}:{get_account_id()}:apis/{self.api_id}"
)
self.arn = f"arn:aws:appsync:{self.region}:{account_id}:apis/{self.api_id}"
self.graphql_schema = None
self.api_keys = dict()
@ -205,6 +204,7 @@ class AppSyncBackend(BaseBackend):
tags,
):
graphql_api = GraphqlAPI(
account_id=self.account_id,
region=self.region_name,
name=name,
authentication_type=authentication_type,

View File

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

View File

@ -1,6 +1,6 @@
import time
from moto.core import BaseBackend, BaseModel, get_account_id
from moto.core import BaseBackend, BaseModel
from moto.core.utils import BackendDict
from uuid import uuid4
@ -10,18 +10,11 @@ class TaggableResourceMixin(object):
# This mixing was copied from Redshift when initially implementing
# 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.resource_name = resource_name
self.tags = tags or []
@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,
)
self.arn = f"arn:aws:athena:{region_name}:{account_id}:{resource_name}"
def create_tags(self, 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):
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.name = name
self.description = description
@ -53,7 +51,12 @@ class DataCatalog(TaggableResourceMixin, BaseModel):
self, athena_backend, name, catalog_type, description, parameters, tags
):
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.name = name
self.type = catalog_type

View File

@ -5,9 +5,12 @@ from .models import athena_backends
class AthenaResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="athena")
@property
def athena_backend(self):
return athena_backends[self.region]
return athena_backends[self.current_account][self.region]
def create_work_group(self):
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 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.ec2 import ec2_backends
from moto.elb import elb_backends
@ -97,7 +97,7 @@ class FakeScalingPolicy(BaseModel):
@property
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):
if self.adjustment_type == "ExactCapacity":
@ -131,6 +131,7 @@ class FakeLaunchConfiguration(CloudFormationModel):
ebs_optimized,
associate_public_ip_address,
block_device_mapping_dict,
account_id,
region_name,
metadata_options,
classic_link_vpc_id,
@ -157,7 +158,7 @@ class FakeLaunchConfiguration(CloudFormationModel):
self.metadata_options = metadata_options
self.classic_link_vpc_id = classic_link_vpc_id
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
def create_from_instance(cls, name, instance, backend):
@ -191,13 +192,13 @@ class FakeLaunchConfiguration(CloudFormationModel):
@classmethod
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"]
instance_profile_name = properties.get("IamInstanceProfile")
backend = autoscaling_backends[region_name]
backend = autoscaling_backends[account_id][region_name]
config = backend.create_launch_configuration(
name=resource_name,
image_id=properties.get("ImageId"),
@ -218,27 +219,32 @@ class FakeLaunchConfiguration(CloudFormationModel):
@classmethod
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(
original_resource.name, cloudformation_json, region_name
original_resource.name, cloudformation_json, account_id, region_name
)
return cls.create_from_cloudformation_json(
new_resource_name, cloudformation_json, region_name
new_resource_name, cloudformation_json, account_id, region_name
)
@classmethod
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:
backend.delete_launch_configuration(resource_name)
except KeyError:
pass
def delete(self, region_name):
backend = autoscaling_backends[region_name]
def delete(self, account_id, region_name):
backend = autoscaling_backends[account_id][region_name]
backend.delete_launch_configuration(self.name)
@property
@ -315,12 +321,12 @@ class FakeScheduledAction(CloudFormationModel):
@classmethod
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"]
backend = autoscaling_backends[region_name]
backend = autoscaling_backends[account_id][region_name]
scheduled_action_name = (
kwargs["LogicalId"]
@ -369,6 +375,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
self.name = name
self._id = str(uuid4())
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)
@ -415,7 +422,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
@property
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):
return [x for x in self.instance_states if x.lifecycle_state == "InService"]
@ -498,7 +505,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
@classmethod
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"]
@ -510,7 +517,7 @@ class FakeAutoScalingGroup(CloudFormationModel):
load_balancer_names = properties.get("LoadBalancerNames", [])
target_group_arns = properties.get("TargetGroupARNs", [])
backend = autoscaling_backends[region_name]
backend = autoscaling_backends[account_id][region_name]
group = backend.create_auto_scaling_group(
name=resource_name,
availability_zones=properties.get("AvailabilityZones", []),
@ -540,27 +547,32 @@ class FakeAutoScalingGroup(CloudFormationModel):
@classmethod
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(
original_resource.name, cloudformation_json, region_name
original_resource.name, cloudformation_json, account_id, region_name
)
return cls.create_from_cloudformation_json(
new_resource_name, cloudformation_json, region_name
new_resource_name, cloudformation_json, account_id, region_name
)
@classmethod
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:
backend.delete_auto_scaling_group(resource_name)
except KeyError:
pass
def delete(self, region_name):
backend = autoscaling_backends[region_name]
def delete(self, account_id, region_name):
backend = autoscaling_backends[account_id][region_name]
backend.delete_auto_scaling_group(self.name)
@property
@ -740,9 +752,9 @@ class AutoScalingBackend(BaseBackend):
self.scheduled_actions = OrderedDict()
self.policies = {}
self.lifecycle_hooks = {}
self.ec2_backend = ec2_backends[region_name]
self.elb_backend = elb_backends[region_name]
self.elbv2_backend = elbv2_backends[region_name]
self.ec2_backend = ec2_backends[self.account_id][region_name]
self.elb_backend = elb_backends[self.account_id][region_name]
self.elbv2_backend = elbv2_backends[self.account_id][region_name]
@staticmethod
def default_vpc_endpoint_service(service_region, zones):
@ -800,6 +812,7 @@ class AutoScalingBackend(BaseBackend):
ebs_optimized=ebs_optimized,
associate_public_ip_address=associate_public_ip_address,
block_device_mapping_dict=block_device_mappings,
account_id=self.account_id,
region_name=self.region_name,
metadata_options=metadata_options,
classic_link_vpc_id=classic_link_vpc_id,

View File

@ -10,9 +10,12 @@ from .models import autoscaling_backends
class AutoScalingResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="autoscaling")
@property
def autoscaling_backend(self):
return autoscaling_backends[self.region]
return autoscaling_backends[self.current_account][self.region]
def create_launch_configuration(self):
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.dynamodb import dynamodb_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 tempfile import TemporaryDirectory
from uuid import uuid4
@ -179,11 +178,13 @@ def _s3_content(key):
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
try:
# 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:
if do_validate_s3():
raise InvalidParameterValueException(
@ -212,18 +213,19 @@ class Permission(CloudFormationModel):
@classmethod
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"]
backend = lambda_backends[region_name]
backend = lambda_backends[account_id][region_name]
fn = backend.get_function(properties["FunctionName"])
fn.policy.add_statement(raw=json.dumps(properties))
return Permission(region=region_name)
class LayerVersion(CloudFormationModel):
def __init__(self, spec, region):
def __init__(self, spec, account_id, region):
# required
self.account_id = account_id
self.region = region
self.name = spec["LayerName"]
self.content = spec["Content"]
@ -248,7 +250,7 @@ class LayerVersion(CloudFormationModel):
self.code_digest,
) = _zipfile_content(self.content["ZipFile"])
else:
key = _validate_s3_bucket_and_key(self.content)
key = _validate_s3_bucket_and_key(account_id, data=self.content)
if key:
(
self.code_bytes,
@ -261,7 +263,7 @@ class LayerVersion(CloudFormationModel):
def arn(self):
if self.version:
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")
@ -297,7 +299,7 @@ class LayerVersion(CloudFormationModel):
@classmethod
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"]
optional_properties = ("Description", "CompatibleRuntimes", "LicenseInfo")
@ -311,16 +313,25 @@ class LayerVersion(CloudFormationModel):
if prop in properties:
spec[prop] = properties[prop]
backend = lambda_backends[region_name]
backend = lambda_backends[account_id][region_name]
layer_version = backend.publish_layer_version(spec)
return layer_version
class LambdaAlias(BaseModel):
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.function_version = function_version
self.description = description
@ -347,11 +358,13 @@ class LambdaAlias(BaseModel):
class Layer(object):
def __init__(self, name, region):
self.region = region
self.name = name
def __init__(self, layer_version: LayerVersion):
self.region = layer_version.region
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.layer_versions = {}
@ -378,16 +391,17 @@ class Layer(object):
class LambdaFunction(CloudFormationModel, DockerModel):
def __init__(self, spec, region, version=1):
def __init__(self, account_id, spec, region, version=1):
DockerModel.__init__(self)
# required
self.account_id = account_id
self.region = region
self.code = spec["Code"]
self.function_name = spec["FunctionName"]
self.handler = spec.get("Handler")
self.role = spec["Role"]
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.policy = None
self.state = "Active"
@ -428,7 +442,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
self.code["UUID"] = str(uuid.uuid4())
self.code["S3Key"] = "{}-{}".format(self.function_name, self.code["UUID"])
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:
(
self.code_bytes,
@ -447,7 +461,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
self.code_size = 0
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"):
@ -459,7 +473,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
def set_version(self, version):
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.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())
def _get_layers_data(self, layers_versions_arns):
backend = lambda_backends[self.region]
backend = lambda_backends[self.account_id][self.region]
layer_versions = [
backend.layers_versions_by_arn(layer_version)
for layer_version in layers_versions_arns
@ -602,7 +616,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
key = None
try:
# 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"]
)
except MissingBucket:
@ -791,7 +805,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
@classmethod
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"]
optional_properties = (
@ -827,7 +841,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
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)
return fn
@ -839,12 +853,17 @@ class LambdaFunction(CloudFormationModel, DockerModel):
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
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()
@classmethod
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"]
original_resource.update_configuration(updated_props)
@ -865,8 +884,8 @@ class LambdaFunction(CloudFormationModel, DockerModel):
zip_output.seek(0)
return zip_output.read()
def delete(self, region):
lambda_backends[region].delete_function(self.function_name)
def delete(self, account_id, region):
lambda_backends[account_id][region].delete_function(self.function_name)
def delete_alias(self, name):
self._aliases.pop(name, None)
@ -874,11 +893,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
def get_alias(self, name):
if name in self._aliases:
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)
def put_alias(self, name, description, function_version, routing_config):
alias = LambdaAlias(
account_id=self.account_id,
region=self.region,
name=name,
function_name=self.function_name,
@ -968,8 +988,8 @@ class EventSourceMapping(CloudFormationModel):
"StateTransitionReason": "User initiated",
}
def delete(self, region_name):
lambda_backend = lambda_backends[region_name]
def delete(self, account_id, region_name):
lambda_backend = lambda_backends[account_id][region_name]
lambda_backend.delete_event_source_mapping(self.uuid)
@staticmethod
@ -983,27 +1003,32 @@ class EventSourceMapping(CloudFormationModel):
@classmethod
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"]
lambda_backend = lambda_backends[region_name]
lambda_backend = lambda_backends[account_id][region_name]
return lambda_backend.create_event_source_mapping(properties)
@classmethod
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"]
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)
@classmethod
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"]
lambda_backend = lambda_backends[region_name]
lambda_backend = lambda_backends[account_id][region_name]
esms = lambda_backend.list_event_source_mappings(
event_source_arn=properties["EventSourceArn"],
function_name=properties["FunctionName"],
@ -1011,7 +1036,7 @@ class EventSourceMapping(CloudFormationModel):
for esm in esms:
if esm.uuid == resource_name:
esm.delete(region_name)
esm.delete(account_id, region_name)
@property
def physical_resource_id(self):
@ -1036,22 +1061,23 @@ class LambdaVersion(CloudFormationModel):
@classmethod
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"]
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}
return LambdaVersion(spec)
class LambdaStorage(object):
def __init__(self, region_name):
def __init__(self, region_name, account_id):
# Format 'func_name' {'versions': []}
self._functions = {}
self._aliases = dict()
self._arns = weakref.WeakValueDictionary()
self.region_name = region_name
self.account_id = account_id
def _get_latest(self, name):
return self._functions[name]["latest"]
@ -1120,7 +1146,7 @@ class LambdaStorage(object):
if name_or_arn.startswith("arn:aws"):
arn = name_or_arn
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:
arn = f"{arn}:{qualifier}"
raise UnknownFunctionException(arn)
@ -1134,10 +1160,11 @@ class LambdaStorage(object):
valid_role = re.match(InvalidRoleFormat.pattern, fn.role)
if valid_role:
account = valid_role.group(2)
if account != get_account_id():
if account != self.account_id:
raise CrossAccountNotAllowed()
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:
raise InvalidParameterValueException(
"The role defined for the function cannot be assumed by Lambda."
@ -1240,9 +1267,7 @@ class LayerStorage(object):
:param layer_version: LayerVersion
"""
if layer_version.name not in self._layers:
self._layers[layer_version.name] = Layer(
layer_version.name, layer_version.region
)
self._layers[layer_version.name] = Layer(layer_version)
self._layers[layer_version.name].attach_version(layer_version)
def list_layers(self):
@ -1328,7 +1353,7 @@ class LambdaBackend(BaseBackend):
def __init__(self, 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._layers = LayerStorage()
@ -1367,7 +1392,12 @@ class LambdaBackend(BaseBackend):
if function_name is None:
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)
@ -1393,7 +1423,8 @@ class LambdaBackend(BaseBackend):
raise RESTError("ResourceNotFoundException", "Invalid FunctionName")
# 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.lambda_event_source_mappings.get("func.function_arn"):
# TODO: Correct exception?
@ -1414,15 +1445,15 @@ class LambdaBackend(BaseBackend):
queue.lambda_event_source_mappings[esm.function_arn] = esm
return esm
for stream in json.loads(
dynamodbstreams_backends[self.region_name].list_streams()
)["Streams"]:
ddbstream_backend = dynamodbstreams_backends[self.account_id][self.region_name]
ddb_backend = dynamodb_backends[self.account_id][self.region_name]
for stream in json.loads(ddbstream_backend.list_streams())["Streams"]:
if stream["StreamArn"] == spec["EventSourceArn"]:
spec.update({"FunctionArn": func.function_arn})
esm = EventSourceMapping(spec)
self._event_source_mappings[esm.uuid] = esm
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
return esm
raise RESTError("ResourceNotFoundException", "Invalid EventSourceArn")
@ -1432,7 +1463,9 @@ class LambdaBackend(BaseBackend):
for param in required:
if not spec.get(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)
return layer_version
@ -1592,7 +1625,7 @@ class LambdaBackend(BaseBackend):
):
data = {
"messageType": "DATA_MESSAGE",
"owner": get_account_id(),
"owner": self.account_id,
"logGroup": log_group_name,
"logStream": log_stream_name,
"subscriptionFilters": [filter_name],

View File

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

View File

@ -26,8 +26,9 @@ def backends():
yield _import_backend(module_name, backends_name)
def unique_backends():
for module_name, backends_name in sorted(set(BACKENDS.values())):
def service_backends():
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)

View File

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

View File

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

View File

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

View File

@ -9,4 +9,4 @@ class BatchSimpleResponse(BatchResponse):
:return: Batch Backend
: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):
def __init__(self):
super().__init__(service_name="budgets")
@property
def backend(self):
return budgets_backends["global"]
return budgets_backends[self.current_account]["global"]
def create_budget(self):
account_id = self._get_param("AccountId")

View File

@ -1,19 +1,21 @@
"""CostExplorerBackend class with methods for supported APIs."""
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 uuid import uuid4
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.rule_version = rule_version
self.rules = rules
self.default_value = default_value
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):
self.rule_version = rule_version
@ -51,7 +53,12 @@ class CostExplorerBackend(BaseBackend):
The EffectiveOn and ResourceTags-parameters are not yet implemented
"""
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
return ccd.arn, ""

View File

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

View File

@ -33,7 +33,7 @@ class CustomModel(CloudFormationModel):
@classmethod
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"]
stack_id = kwargs["StackId"]
@ -41,7 +41,7 @@ class CustomModel(CloudFormationModel):
properties = cloudformation_json["Properties"]
service_token = properties["ServiceToken"]
backend = lambda_backends[region_name]
backend = lambda_backends[account_id][region_name]
fn = backend.get_function(service_token)
request_id = str(uuid4())
@ -52,7 +52,7 @@ class CustomModel(CloudFormationModel):
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)
# 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.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 (
iso_8601_datetime_with_milliseconds,
iso_8601_datetime_without_milliseconds,
@ -31,6 +31,7 @@ class FakeStackSet(BaseModel):
def __init__(
self,
stackset_id,
account_id,
name,
template,
region="us-east-1",
@ -42,13 +43,14 @@ class FakeStackSet(BaseModel):
execution_role="AWSCloudFormationStackSetExecutionRole",
):
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.template = template
self.description = description
self.parameters = parameters
self.tags = tags
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.status = status
self.instances = FakeStackInstances(parameters, self.id, self.name)
@ -218,6 +220,7 @@ class FakeStack(BaseModel):
name,
template,
parameters,
account_id,
region_name,
notification_arns=None,
tags=None,
@ -226,6 +229,7 @@ class FakeStack(BaseModel):
):
self.stack_id = stack_id
self.name = name
self.account_id = account_id
self.template = template
if template != {}:
self._parse_template()
@ -267,9 +271,10 @@ class FakeStack(BaseModel):
self.name,
self.parameters,
self.tags,
self.region_name,
self.template_dict,
self.cross_stack_resources,
account_id=self.account_id,
region_name=self.region_name,
template=self.template_dict,
cross_stack_resources=self.cross_stack_resources,
)
resource_map.load()
return resource_map
@ -296,7 +301,7 @@ class FakeStack(BaseModel):
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)
def _add_resource_event(
@ -486,7 +491,7 @@ class FakeEvent(BaseModel):
self.event_id = uuid.uuid4()
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}'
Timestamp='{timestamp}'
EventId='{event_id}'
@ -502,7 +507,7 @@ ClientRequestToken='{client_request_token}'""".format(
timestamp=iso_8601_datetime_with_milliseconds(self.timestamp),
event_id=self.event_id,
logical_resource_id=self.logical_resource_id,
account_id=get_account_id(),
account_id=account_id,
resource_properties=self.resource_properties,
resource_status=self.resource_status,
resource_status_reason=self.resource_status_reason,
@ -512,7 +517,7 @@ ClientRequestToken='{client_request_token}'""".format(
)
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
)
@ -584,6 +589,7 @@ class CloudFormationBackend(BaseBackend):
stackset_id = generate_stackset_id(name)
new_stackset = FakeStackSet(
stackset_id=stackset_id,
account_id=self.account_id,
name=name,
template=template,
parameters=parameters,
@ -671,12 +677,13 @@ class CloudFormationBackend(BaseBackend):
tags=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(
stack_id=stack_id,
name=name,
template=template,
parameters=parameters,
account_id=self.account_id,
region_name=self.region_name,
notification_arns=notification_arns,
tags=tags,
@ -712,12 +719,13 @@ class CloudFormationBackend(BaseBackend):
else:
raise ValidationError(stack_name)
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_id=stack_id,
name=stack_name,
template={},
parameters=parameters,
account_id=self.account_id,
region_name=self.region_name,
notification_arns=notification_arns,
tags=tags,
@ -729,7 +737,9 @@ class CloudFormationBackend(BaseBackend):
"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(
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
from moto.core import get_account_id, CloudFormationModel
from moto.core import CloudFormationModel
from moto.s3.models import s3_backends
from moto.s3.utils import bucket_and_name_from_url
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
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")
if condition and not resources_map.lazy_condition_map[condition]:
# 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,
}
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.logical_resource_id = logical_id
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(
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,
new_resource_name=new_resource_name,
cloudformation_json=resource_json,
account_id=account_id,
region_name=region_name,
)
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
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_class = resource_class_from_type(resource_type)
if not hasattr(
resource_class.delete_from_cloudformation_json, "__isabstractmethod__"
):
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,
tags,
region_name,
account_id,
template,
cross_stack_resources,
):
self._template = template
self._resource_json_map = template["Resources"] if template != {} else {}
self._account_id = account_id
self._region_name = region_name
self.input_parameters = parameters
self.tags = copy.deepcopy(tags)
@ -453,7 +460,7 @@ class ResourceMap(collections_abc.Mapping):
# Create the default resources
self._parsed_resources = {
"AWS::AccountId": get_account_id(),
"AWS::AccountId": account_id,
"AWS::Region": self._region_name,
"AWS::StackId": stack_id,
"AWS::StackName": stack_name,
@ -473,7 +480,11 @@ class ResourceMap(collections_abc.Mapping):
if not resource_json:
raise KeyError(resource_logical_id)
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:
self._parsed_resources[resource_logical_id] = new_resource
@ -528,14 +539,18 @@ class ResourceMap(collections_abc.Mapping):
if name == "AWS::Include":
location = params["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))
def parse_ssm_parameter(self, value, value_type):
# The Value in SSM parameters is the SSM parameter path
# we need to use ssm_backend to retrieve the
# 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
if value_type.find("List") > 0:
return actual_value.split(",")
@ -646,9 +661,9 @@ class ResourceMap(collections_abc.Mapping):
instance = self[resource]
if isinstance(instance, TaggedEC2Resource):
self.tags["aws:cloudformation:logical-id"] = resource
ec2_models.ec2_backends[self._region_name].create_tags(
[instance.physical_resource_id], self.tags
)
ec2_models.ec2_backends[self._account_id][
self._region_name
].create_tags([instance.physical_resource_id], self.tags)
if instance and not instance.is_created():
all_resources_ready = False
return all_resources_ready
@ -716,7 +731,9 @@ class ResourceMap(collections_abc.Mapping):
].physical_resource_id
else:
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._template = template
@ -740,7 +757,11 @@ class ResourceMap(collections_abc.Mapping):
resource_json = self._resource_json_map[logical_name]
try:
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:
# skip over dependency violations, and try again in a
@ -765,7 +786,7 @@ class ResourceMap(collections_abc.Mapping):
and parsed_resource is not None
):
if parsed_resource and hasattr(parsed_resource, "delete"):
parsed_resource.delete(self._region_name)
parsed_resource.delete(self._account_id, self._region_name)
else:
if hasattr(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(
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)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
"""Exceptions raised by the cloudtrail service."""
from moto.core import get_account_id
from moto.core.exceptions import JsonRESTError
@ -27,10 +26,10 @@ class InsufficientSnsTopicPolicyException(JsonRESTError):
class TrailNotFoundException(JsonRESTError):
code = 400
def __init__(self, name):
def __init__(self, account_id, name):
super().__init__(
"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
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.utilities.tagging_service import TaggingService
from .exceptions import (
@ -74,6 +74,7 @@ class TrailStatus(object):
class Trail(BaseModel):
def __init__(
self,
account_id,
region_name,
trail_name,
bucket_name,
@ -87,6 +88,7 @@ class Trail(BaseModel):
cw_role_arn,
kms_key_id,
):
self.account_id = account_id
self.region_name = region_name
self.trail_name = trail_name
self.bucket_name = bucket_name
@ -109,12 +111,12 @@ class Trail(BaseModel):
@property
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
def topic_arn(self):
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
def check_name(self):
@ -133,7 +135,7 @@ class Trail(BaseModel):
from moto.s3.models import s3_backends
try:
s3_backends["global"].get_bucket(self.bucket_name)
s3_backends[self.account_id]["global"].get_bucket(self.bucket_name)
except Exception:
raise S3BucketDoesNotExistException(
f"S3 bucket {self.bucket_name} does not exist!"
@ -143,7 +145,7 @@ class Trail(BaseModel):
if self.sns_topic_name:
from moto.sns import sns_backends
sns_backend = sns_backends[self.region_name]
sns_backend = sns_backends[self.account_id][self.region_name]
try:
sns_backend.get_topic(self.topic_arn)
except Exception:
@ -263,6 +265,7 @@ class CloudTrailBackend(BaseBackend):
tags_list,
):
trail = Trail(
self.account_id,
self.region_name,
name,
bucket_name,
@ -288,7 +291,7 @@ class CloudTrailBackend(BaseBackend):
for trail in self.trails.values():
if trail.arn == name_or_arn:
return trail
raise TrailNotFoundException(name_or_arn)
raise TrailNotFoundException(account_id=self.account_id, name=name_or_arn)
def get_trail_status(self, name):
if len(name) < 3:
@ -304,9 +307,9 @@ class CloudTrailBackend(BaseBackend):
if not trail_name:
# This particular method returns the ARN as part of the error message
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]
return trail.status

View File

@ -9,10 +9,13 @@ from .exceptions import InvalidParameterCombinationException
class CloudTrailResponse(BaseResponse):
"""Handler for CloudTrail requests and responses."""
def __init__(self):
super().__init__(service_name="cloudtrail")
@property
def cloudtrail_backend(self):
"""Return backend instance specific for this region."""
return cloudtrail_backends[self.region]
return cloudtrail_backends[self.current_account][self.region]
def create_trail(self):
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 dateutil import parser
from moto.core import get_account_id
from ..utilities.tagging_service import TaggingService
_EMPTY_LIST = tuple()
@ -103,6 +102,7 @@ def daterange(start, stop, step=timedelta(days=1), inclusive=False):
class FakeAlarm(BaseModel):
def __init__(
self,
account_id,
region_name,
name,
namespace,
@ -129,7 +129,7 @@ class FakeAlarm(BaseModel):
):
self.region_name = region_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.metric_name = metric_name
self.metric_data_queries = metric_data_queries
@ -238,9 +238,9 @@ class MetricDatum(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
self.arn = make_arn_for_dashboard(get_account_id(), name)
self.arn = make_arn_for_dashboard(account_id, name)
self.name = name
self.body = body
self.last_modified = datetime.now()
@ -327,7 +327,7 @@ class CloudWatchBackend(BaseBackend):
providers = CloudWatchMetricProvider.__subclasses__()
md = []
for provider in providers:
md.extend(provider.get_cloudwatch_metrics())
md.extend(provider.get_cloudwatch_metrics(self.account_id))
return md
def put_metric_alarm(
@ -370,6 +370,7 @@ class CloudWatchBackend(BaseBackend):
)
alarm = FakeAlarm(
account_id=self.account_id,
region_name=self.region_name,
name=name,
namespace=namespace,
@ -590,7 +591,7 @@ class CloudWatchBackend(BaseBackend):
return self.metric_data + self.aws_metric_data
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=""):
for key, value in self.dashboards.items():

View File

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

View File

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

View File

@ -5,7 +5,6 @@ from .exceptions import (
ResourceAlreadyExistsException,
ResourceNotFoundException,
)
from moto.core import get_account_id
import json
import re
@ -29,11 +28,8 @@ def _validate_required_params_source(source):
raise InvalidInputException("Project source location is required")
def _validate_required_params_service_role(service_role):
if (
"arn:aws:iam::{0}:role/service-role/".format(get_account_id())
not in service_role
):
def _validate_required_params_service_role(account_id, service_role):
if f"arn:aws:iam::{account_id}:role/service-role/" not in service_role:
raise InvalidInputException(
"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):
@property
def codebuild_backend(self):
return codebuild_backends[self.region]
return codebuild_backends[self.current_account][self.region]
def list_builds_for_project(self):
_validate_required_params_project_name(self._get_param("projectName"))
@ -110,7 +106,7 @@ class CodeBuildResponse(BaseResponse):
):
raise ResourceNotFoundException(
"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):
_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_environment(self._get_param("environment"))
_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():
raise ResourceAlreadyExistsException(
"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(
"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.utils import iso_8601_datetime_with_milliseconds, BackendDict
from datetime import datetime
from moto.core import get_account_id
from .exceptions import RepositoryDoesNotExistException, RepositoryNameExistsException
import uuid
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())
self.repository_metadata = dict()
self.repository_metadata["repositoryName"] = repository_name
@ -25,10 +24,10 @@ class CodeCommit(BaseModel):
self.repository_metadata["lastModifiedDate"] = current_date
self.repository_metadata["repositoryDescription"] = repository_description
self.repository_metadata["repositoryId"] = str(uuid.uuid4())
self.repository_metadata["Arn"] = "arn:aws:codecommit:{0}:{1}:{2}".format(
region, get_account_id(), repository_name
)
self.repository_metadata["accountId"] = get_account_id()
self.repository_metadata[
"Arn"
] = f"arn:aws:codecommit:{region}:{account_id}:{repository_name}"
self.repository_metadata["accountId"] = account_id
class CodeCommitBackend(BaseBackend):
@ -49,7 +48,7 @@ class CodeCommitBackend(BaseBackend):
raise RepositoryNameExistsException(repository_name)
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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,12 @@ from .utils import get_random_identity_id
class CognitoIdentityResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="cognito-identity")
@property
def backend(self):
return cognitoidentity_backends[self.region]
return cognitoidentity_backends[self.current_account][self.region]
def create_identity_pool(self):
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"))
def get_open_id_token_for_developer_identity(self):
return cognitoidentity_backends[
self.region
].get_open_id_token_for_developer_identity(
return self.backend.get_open_id_token_for_developer_identity(
self._get_param("IdentityId") or get_random_identity_id(self.region)
)

View File

@ -9,7 +9,6 @@ import random
from jose import jws
from collections import OrderedDict
from moto.core import BaseBackend, BaseModel
from moto.core import get_account_id
from moto.core.utils import BackendDict
from .exceptions import (
GroupExistsException,
@ -371,16 +370,15 @@ class CognitoIdpUserPool(BaseModel):
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
user_pool_id = generate_id(
get_cognito_idp_user_pool_id_strategy(), region, name, extended_config
)
self.id = "{}_{}".format(self.region, user_pool_id)[: self.MAX_ID_LENGTH]
self.arn = "arn:aws:cognito-idp:{}:{}:userpool/{}".format(
self.region, get_account_id(), self.id
)
self.arn = f"arn:aws:cognito-idp:{self.region}:{account_id}:userpool/{self.id}"
self.name = name
self.status = None
@ -445,7 +443,7 @@ class CognitoIdpUserPool(BaseModel):
@property
def backend(self):
return cognitoidp_backends[self.region]
return cognitoidp_backends[self.account_id][self.region]
@property
def domain(self):
@ -862,7 +860,9 @@ class CognitoIdpBackend(BaseBackend):
# User pool
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
return user_pool
@ -1833,24 +1833,24 @@ class RegionAgnosticBackend:
# This backend will cycle through all backends as a workaround
def _find_backend_by_access_token(self, access_token):
account_specific_backends = cognitoidp_backends[get_account_id()]
for region, backend in account_specific_backends.items():
if region == "global":
continue
for p in backend.user_pools.values():
if access_token in p.access_tokens:
return backend
return account_specific_backends["us-east-1"]
for account_specific_backends in cognitoidp_backends.values():
for region, backend in account_specific_backends.items():
if region == "global":
continue
for p in backend.user_pools.values():
if access_token in p.access_tokens:
return backend
return backend
def _find_backend_for_clientid(self, client_id):
account_specific_backends = cognitoidp_backends[get_account_id()]
for region, backend in account_specific_backends.items():
if region == "global":
continue
for p in backend.user_pools.values():
if client_id in p.clients:
return backend
return account_specific_backends["us-east-1"]
for account_specific_backends in cognitoidp_backends.values():
for region, backend in account_specific_backends.items():
if region == "global":
continue
for p in backend.user_pools.values():
if client_id in p.clients:
return backend
return backend
def sign_up(self, client_id, username, password, attributes):
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
# specified in the host header. Some endpoints (change password, confirm forgot
# password) have no authorization header from which to extract the region.
def find_region_by_value(key, value):
account_specific_backends = cognitoidp_backends[get_account_id()]
for region in account_specific_backends:
backend = cognitoidp_backends[region]
for user_pool in backend.user_pools.values():
if key == "client_id" and value in user_pool.clients:
return region
def find_account_region_by_value(key, value):
for account_id, account_specific_backend in cognitoidp_backends.items():
for region, backend in account_specific_backend.items():
for user_pool in backend.user_pools.values():
if key == "client_id" and value in user_pool.clients:
return account_id, region
if key == "access_token" and value in user_pool.access_tokens:
return region
if key == "access_token" and value in user_pool.access_tokens:
return account_id, region
# If we can't find the `client_id` or `access_token`, we just pass
# back a default backend region, which will raise the appropriate
# 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 .models import (
cognitoidp_backends,
find_region_by_value,
find_account_region_by_value,
RegionAgnosticBackend,
UserStatus,
)
@ -16,13 +16,16 @@ region_agnostic_backend = RegionAgnosticBackend()
class CognitoIdpResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="cognito-idp")
@property
def parameters(self):
return json.loads(self.body)
@property
def backend(self):
return cognitoidp_backends[self.region]
return cognitoidp_backends[self.current_account][self.region]
# User pool
def create_user_pool(self):
@ -138,9 +141,7 @@ class CognitoIdpResponse(BaseResponse):
user_pool_id = self._get_param("UserPoolId")
max_results = self._get_param("MaxResults")
next_token = self._get_param("NextToken")
user_pool_clients, next_token = cognitoidp_backends[
self.region
].list_user_pool_clients(
user_pool_clients, next_token = self.backend.list_user_pool_clients(
user_pool_id, max_results=max_results, next_token=next_token
)
response = {
@ -189,9 +190,7 @@ class CognitoIdpResponse(BaseResponse):
user_pool_id = self._get_param("UserPoolId")
max_results = self._get_param("MaxResults")
next_token = self._get_param("NextToken")
identity_providers, next_token = cognitoidp_backends[
self.region
].list_identity_providers(
identity_providers, next_token = self.backend.list_identity_providers(
user_pool_id, max_results=max_results, next_token=next_token
)
response = {
@ -457,11 +456,10 @@ class CognitoIdpResponse(BaseResponse):
def forgot_password(self):
client_id = self._get_param("ClientId")
username = self._get_param("Username")
region = find_region_by_value("client_id", client_id)
print(f"Region: {region}")
confirmation_code, response = cognitoidp_backends[region].forgot_password(
client_id, username
)
account, region = find_account_region_by_value("client_id", client_id)
confirmation_code, response = cognitoidp_backends[account][
region
].forgot_password(client_id, username)
self.response_headers[
"x-moto-forgot-password-confirmation-code"
] = confirmation_code
@ -476,8 +474,8 @@ class CognitoIdpResponse(BaseResponse):
username = self._get_param("Username")
password = self._get_param("Password")
confirmation_code = self._get_param("ConfirmationCode")
region = find_region_by_value("client_id", client_id)
cognitoidp_backends[region].confirm_forgot_password(
account, region = find_account_region_by_value("client_id", client_id)
cognitoidp_backends[account][region].confirm_forgot_password(
client_id, username, password, confirmation_code
)
return ""
@ -487,8 +485,8 @@ class CognitoIdpResponse(BaseResponse):
access_token = self._get_param("AccessToken")
previous_password = self._get_param("PreviousPassword")
proposed_password = self._get_param("ProposedPassword")
region = find_region_by_value("access_token", access_token)
cognitoidp_backends[region].change_password(
account, region = find_account_region_by_value("access_token", access_token)
cognitoidp_backends[account][region].change_password(
access_token, previous_password, proposed_password
)
return ""

View File

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

View File

@ -4,9 +4,12 @@ from .models import config_backends
class ConfigResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="config")
@property
def config_backend(self):
return config_backends[self.region]
return config_backends[self.current_account][self.region]
def put_configuration_recorder(self):
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 .common_models import BaseModel # noqa
from .common_models import CloudFormationModel, CloudWatchMetricProvider # noqa

View File

@ -37,10 +37,10 @@ class CloudFormationModel(BaseModel):
@classmethod
@abstractmethod
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:
# cls, resource_name, cloudformation_json, region_name
# cls, resource_name, cloudformation_json, account_id, region_name
# Extract the resource parameters from the cloudformation json
# and return an instance of the resource class
pass
@ -48,10 +48,15 @@ class CloudFormationModel(BaseModel):
@classmethod
@abstractmethod
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:
# 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,
# delete the old resource and return the new one. Optionally inspect
# the change in parameters and no-op when nothing has changed.
@ -60,10 +65,10 @@ class CloudFormationModel(BaseModel):
@classmethod
@abstractmethod
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:
# cls, resource_name, cloudformation_json, region_name
# cls, resource_name, cloudformation_json, account_id, region_name
# Extract the resource parameters from the cloudformation json
# and delete the resource. Do not include a return statement.
pass
@ -83,6 +88,7 @@ class ConfigQueryModel:
def list_config_service_resources(
self,
account_id,
resource_ids,
resource_name,
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
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_name: The individual name of a resource
:param limit: How many per page
@ -140,7 +147,12 @@ class ConfigQueryModel:
raise NotImplementedError()
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.
@ -160,6 +172,7 @@ class ConfigQueryModel:
from all resources in all regions for a given resource type*.
...
:param account_id:
:param resource_id:
:param resource_name:
:param backend_region:
@ -172,5 +185,5 @@ class ConfigQueryModel:
class CloudWatchMetricProvider(object):
@staticmethod
@abstractmethod
def get_cloudwatch_metrics():
def get_cloudwatch_metrics(account_id):
pass

View File

@ -23,18 +23,7 @@ from .custom_responses_mock import (
)
from .utils import convert_flask_to_responses_response
ACCOUNT_ID = os.environ.get("MOTO_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()
DEFAULT_ACCOUNT_ID = "123456789012"
class BaseMockAWS:
@ -42,23 +31,25 @@ class BaseMockAWS:
mocks_active = False
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
self.backends = backends
self.backends_for_urls = {}
default_backends = {
"instance_metadata": instance_metadata_backend,
"moto_api": moto_api_backend,
}
if "us-east-1" in self.backends:
self.backends_for_urls = []
default_account_id = DEFAULT_ACCOUNT_ID
default_backends = [
instance_metadata_backends[default_account_id]["global"],
moto_api_backend,
]
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
self.backends_for_urls["us-east-1"] = self.backends["us-east-1"]
elif "global" in self.backends:
self.backends_for_urls.append(backend_default_account["us-east-1"])
elif "global" in backend_default_account:
# 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.update(default_backends)
self.backends_for_urls.append(backend_default_account["global"])
self.backends_for_urls.extend(default_backends)
self.FAKE_KEYS = {
"AWS_ACCESS_KEY_ID": "foobar_key",
@ -283,7 +274,7 @@ class BotocoreEventMockAWS(BaseMockAWS):
def enable_patching(self, reset=True): # pylint: disable=unused-argument
botocore_stubber.enabled = True
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():
pattern = re.compile(key)
botocore_stubber.register_response(method, pattern, value)
@ -295,7 +286,7 @@ class BotocoreEventMockAWS(BaseMockAWS):
for method in RESPONSES_METHODS:
# 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():
responses_mock.add(
CallbackResponse(

View File

@ -3,6 +3,7 @@ from collections import defaultdict
import datetime
import json
import logging
import os
import re
import requests
@ -137,7 +138,11 @@ class ActionAuthenticatorMixin(object):
>= settings.INITIAL_NO_AUTH_ACTION_COUNT
):
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_action_permitted()
@ -215,6 +220,10 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
)
aws_service_spec = None
def __init__(self, service_name=None):
super().__init__()
self.service_name = service_name
@classmethod
def dispatch(cls, *args, **kwargs):
return cls()._dispatch(*args, **kwargs)
@ -295,6 +304,18 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
self.headers["host"] = urlparse(full_url).netloc
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):
url_match = self.region_regex.search(full_url)
user_agent_match = self.region_from_useragent_regex.search(
@ -313,7 +334,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
region = self.default_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
"""
@ -323,10 +344,24 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
return match.group(1)
if self.querystring.get("AWSAccessKeyId"):
return self.querystring.get("AWSAccessKeyId")
return self.querystring.get("AWSAccessKeyId")[0]
else:
# Should we raise an unauthorized exception instead?
return "111122223333"
return "AKIAEXAMPLE"
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):
self.setup_class(request, full_url, headers)
@ -372,10 +407,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
# service response class should have 'SERVICE_NAME' class member,
# if you want to get action from method and url
if not hasattr(self, "SERVICE_NAME"):
return None
service = self.SERVICE_NAME
conn = boto3.client(service, region_name=self.region)
conn = boto3.client(self.service_name, region_name=self.region)
# make cache if it does not exist yet
if not hasattr(self, "method_urls"):
@ -396,15 +428,15 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
def _get_action(self):
action = self.querystring.get("Action", [""])[0]
if not action: # Some services use a header for the action
# Headers are case-insensitive. Probably a better way to do this.
match = self.headers.get("x-amz-target") or self.headers.get("X-Amz-Target")
if match:
action = match.split(".")[-1]
if action:
return action
# Some services use a header for the action
# Headers are case-insensitive. Probably a better way to do this.
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
if not action:
return self._get_action_from_method_and_request_uri(self.method, self.path)
return action
return self._get_action_from_method_and_request_uri(self.method, self.path)
def call_action(self):
headers = self.response_headers

View File

@ -1,4 +1,4 @@
from functools import wraps
from functools import lru_cache, wraps
import binascii
import datetime
@ -11,6 +11,7 @@ from boto3 import Session
from moto.settings import allow_unknown_region
from threading import RLock
from urllib.parse import urlparse
from uuid import uuid4
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")
)
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):
for region_specific_backend in self.values():
@ -444,6 +459,7 @@ class AccountSpecificBackend(dict):
def __contains__(self, region):
return region in self.regions or region in self.keys()
@lru_cache()
def __getitem__(self, region_name):
if region_name in self.keys():
return super().__getitem__(region_name)
@ -466,14 +482,6 @@ class BackendDict(dict):
Format:
[account_id: str]: AccountSpecificBackend
[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__(
@ -483,46 +491,25 @@ class BackendDict(dict):
self.service_name = service_name
self._use_boto3_regions = use_boto3_regions
self._additional_regions = additional_regions
self._id = str(uuid4())
def __contains__(self, account_id_or_region):
"""
Possible data access patterns:
backend_dict[account_id][region_name]
backend_dict[region_name]
backend_dict[unknown_region]
def __hash__(self):
# Required for the LRUcache to work.
# service_name is enough to determine uniqueness - other properties are dependent
return hash(self._id)
The latter two will be phased out in the future, and we can remove this method.
"""
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 __eq__(self, other):
return other and isinstance(other, BackendDict) and other._id == self._id
def get(self, account_id_or_region, if_none=None):
if self.__contains__(account_id_or_region):
return self.__getitem__(account_id_or_region)
return if_none
def __ne__(self, other):
return not self.__eq__(other)
def __getitem__(self, account_id_or_region):
"""
Possible data access patterns:
backend_dict[account_id][region_name]
backend_dict[region_name]
backend_dict[unknown_region]
@lru_cache()
def __getitem__(self, account_id) -> AccountSpecificBackend:
self._create_account_specific_backend(account_id)
return super().__getitem__(account_id)
The latter two will be phased out in the future.
"""
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):
def _create_account_specific_backend(self, account_id) -> None:
with backend_lock:
if account_id not in self.keys():
self[account_id] = AccountSpecificBackend(

View File

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

View File

@ -83,9 +83,9 @@ class Pipeline(CloudFormationModel):
@classmethod
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"]
cloudformation_unique_id = "cf-" + resource_name

View File

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

View File

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

View File

@ -1,5 +1,5 @@
"""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.moto_api import state_manager
from moto.moto_api._internal.managed_state_model import ManagedState
@ -68,6 +68,7 @@ class DaxEndpoint:
class DaxCluster(BaseModel, ManagedState):
def __init__(
self,
account_id,
region,
name,
description,
@ -85,7 +86,7 @@ class DaxCluster(BaseModel, ManagedState):
# Set internal properties
self.name = name
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.replication_factor = replication_factor
self.cluster_hex = get_random_hex(6)
@ -187,6 +188,7 @@ class DAXBackend(BaseBackend):
AvailabilityZones, SubnetGroupNames, SecurityGroups, PreferredMaintenanceWindow, NotificationTopicArn, ParameterGroupName
"""
cluster = DaxCluster(
account_id=self.account_id,
region=self.region_name,
name=cluster_name,
description=description,

View File

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

View File

@ -1,7 +1,7 @@
import json
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 .exceptions import (
@ -46,6 +46,7 @@ class DatabaseMigrationServiceBackend(BaseBackend):
migration_type=migration_type,
table_mappings=table_mappings,
replication_task_settings=replication_task_settings,
account_id=self.account_id,
region_name=self.region_name,
)
@ -106,6 +107,7 @@ class FakeReplicationTask(BaseModel):
target_endpoint_arn,
table_mappings,
replication_task_settings,
account_id,
region_name,
):
self.id = replication_task_identifier
@ -117,18 +119,13 @@ class FakeReplicationTask(BaseModel):
self.table_mappings = table_mappings
self.replication_task_settings = replication_task_settings
self.arn = f"arn:aws:dms:{region_name}:{account_id}:task:{self.id}"
self.status = "creating"
self.creation_date = datetime.utcnow()
self.start_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):
start_date = self.start_date.isoformat() if self.start_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):
SERVICE_NAME = "dms"
def __init__(self):
super().__init__(service_name="dms")
@property
def dms_backend(self):
return dms_backends[self.region]
return dms_backends[self.current_account][self.region]
def create_replication_task(self):
replication_task_identifier = self._get_param("ReplicationTaskIdentifier")

View File

@ -46,6 +46,7 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
def __init__(
self,
account_id,
region,
name,
password,
@ -57,6 +58,7 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
description=None,
edition=None,
): # pylint: disable=too-many-arguments
self.account_id = account_id
self.region = region
self.name = name
self.password = password
@ -101,7 +103,9 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
def create_security_group(self, vpc_id):
"""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",
description=(
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):
"""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):
"""Return ENI ids and primary addresses created for each subnet."""
eni_ids = []
subnet_ips = []
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,
private_ip_address=None,
group_ids=[security_group_id],
@ -133,7 +141,7 @@ class Directory(BaseModel): # pylint: disable=too-many-instance-attributes
def delete_eni(self):
"""Delete ENI for each subnet and the security group."""
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):
"""Change default alias to given alias."""
@ -192,8 +200,7 @@ class DirectoryServiceBackend(BaseBackend):
service_region, zones, "ds"
)
@staticmethod
def _verify_subnets(region, vpc_settings):
def _verify_subnets(self, region, vpc_settings):
"""Verify subnets are valid, else raise an exception.
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
# be valid and in different availability zones.
try:
subnets = ec2_backends[region].get_all_subnets(
subnets = ec2_backends[self.account_id][region].get_all_subnets(
subnet_ids=vpc_settings["SubnetIds"]
)
except InvalidSubnetIdError as exc:
@ -223,7 +230,7 @@ class DirectoryServiceBackend(BaseBackend):
"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]:
raise ClientException("Invalid VPC ID.")
vpc_settings["AvailabilityZones"] = regions
@ -274,6 +281,7 @@ class DirectoryServiceBackend(BaseBackend):
raise DirectoryLimitExceededException("Tag Limit is exceeding")
directory = Directory(
self.account_id,
region,
name,
password,
@ -319,6 +327,7 @@ class DirectoryServiceBackend(BaseBackend):
raise DirectoryLimitExceededException("Tag Limit is exceeding")
directory = Directory(
self.account_id,
region,
name,
password,
@ -400,6 +409,7 @@ class DirectoryServiceBackend(BaseBackend):
raise DirectoryLimitExceededException("Tag Limit is exceeding")
directory = Directory(
self.account_id,
region,
name,
password,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -70,7 +70,7 @@ class DynamoDBStreamsBackend(BaseBackend):
@property
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):
table_name = arn.split(":", 6)[5].split("/")[1]

View File

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

View File

@ -1,6 +1,6 @@
"""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.ec2 import ec2_backends
from moto.ec2.models.elastic_block_store import Snapshot
@ -17,7 +17,8 @@ class Block(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.status = "pending"
self.start_time = unix_time()
@ -39,7 +40,7 @@ class EBSSnapshot(BaseModel):
def to_json(self):
return {
"SnapshotId": self.snapshot_id,
"OwnerId": ACCOUNT_ID,
"OwnerId": self.account_id,
"Status": self.status,
"StartTime": self.start_time,
"VolumeSize": self.volume_size,
@ -58,7 +59,7 @@ class EBSBackend(BaseBackend):
@property
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):
zone_name = f"{self.region_name}a"
@ -69,7 +70,7 @@ class EBSBackend(BaseBackend):
if tags:
tags = {tag["Key"]: tag["Value"] for tag in 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
return ebs_snapshot

View File

@ -8,10 +8,13 @@ from .models import ebs_backends
class EBSResponse(BaseResponse):
"""Handler for EBS requests and responses."""
def __init__(self):
super().__init__(service_name="ebs")
@property
def ebs_backend(self):
"""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):
self.setup_class(request, full_url, headers)

View File

@ -1,5 +1,4 @@
from .models import ec2_backends
from ..core.models import base_decorator
ec2_backend = ec2_backends["us-east-1"]
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.utils import BackendDict
from ..exceptions import (
@ -55,8 +54,6 @@ from ..utils import (
get_prefix,
)
OWNER_ID = get_account_id()
def validate_resource_ids(resource_ids):
if not resource_ids:
@ -72,15 +69,15 @@ class SettingsBackend:
self.ebs_encryption_by_default = False
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
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
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

View File

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

View File

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

View File

@ -42,11 +42,11 @@ class ElasticAddress(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
properties = cloudformation_json.get("Properties")
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 .core import TaggedEC2Resource
from .security_groups import SecurityGroup
@ -134,7 +134,7 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
@property
def owner_id(self):
return get_account_id()
return self.ec2_backend.account_id
@property
def association(self):
@ -160,7 +160,7 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
@ -168,7 +168,7 @@ class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
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")
if subnet_id:
subnet = ec2_backend.get_subnet(subnet_id)

View File

@ -58,7 +58,7 @@ class FlowLogs(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
@ -74,7 +74,7 @@ class FlowLogs(TaggedEC2Resource, CloudFormationModel):
log_format = properties.get("LogFormat")
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(
resource_type,
resource_id,
@ -219,7 +219,7 @@ class FlowLogsBackend:
arn = log_destination.split(":", 5)[5]
try:
s3_backends["global"].get_bucket(arn)
s3_backends[self.account_id]["global"].get_bucket(arn)
except MissingBucket:
# Instead of creating FlowLog report
# the unsuccessful status for the
@ -242,7 +242,9 @@ class FlowLogsBackend:
try:
# Need something easy to check the group exists.
# 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:
deliver_logs_status = "FAILED"
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 ..exceptions import (
IncorrectStateIamProfileAssociationError,
@ -10,8 +9,6 @@ from ..utils import (
filter_iam_instance_profiles,
)
OWNER_ID = get_account_id()
class IamInstanceProfileAssociation(CloudFormationModel):
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()
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():
@ -101,7 +98,7 @@ class IamInstanceProfileAssociationBackend:
iam_instance_profile_arn=None,
):
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

View File

@ -4,7 +4,6 @@ from collections import OrderedDict
from datetime import datetime
from moto import settings
from moto.core import get_account_id
from moto.core import CloudFormationModel
from moto.core.utils import camelcase_to_underscores
from moto.ec2.models.fleets import Fleet
@ -70,7 +69,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
super().__init__()
self.ec2_backend = ec2_backend
self.id = random_instance_id()
self.owner_id = get_account_id()
self.owner_id = ec2_backend.account_id
self.lifecycle = kwargs.get("lifecycle")
nics = kwargs.get("nics", {})
@ -265,13 +264,13 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
@classmethod
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
properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
security_group_ids = properties.get("SecurityGroups", [])
group_names = [
ec2_backend.get_security_group_from_id(group_id).name
@ -307,11 +306,11 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
@classmethod
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
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
all_instances = ec2_backend.all_instances()
# 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"
and tag["value"] == logical_id
):
instance.delete(region_name)
instance.delete(account_id, region_name)
@property
def physical_resource_id(self):
@ -360,7 +359,7 @@ class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
def is_running(self):
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()
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 ..exceptions import (
@ -80,7 +80,7 @@ class InternetGateway(TaggedEC2Resource, CloudFormationModel):
@property
def owner_id(self):
return get_account_id()
return self.ec2_backend.account_id
@staticmethod
def cloudformation_name_type():
@ -93,11 +93,11 @@ class InternetGateway(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
return ec2_backend.create_internet_gateway()
@property

View File

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

View File

@ -69,11 +69,11 @@ class NatGateway(CloudFormationModel, TaggedEC2Resource):
@classmethod
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
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
nat_gateway = ec2_backend.create_nat_gateway(
cloudformation_json["Properties"]["SubnetId"],
cloudformation_json["Properties"]["AllocationId"],

View File

@ -1,4 +1,3 @@
from moto.core import get_account_id
from ..exceptions import (
InvalidNetworkAclIdError,
InvalidRouteTableIdError,
@ -12,9 +11,6 @@ from ..utils import (
)
OWNER_ID = get_account_id()
class NetworkAclBackend:
def __init__(self):
self.network_acls = {}
@ -210,12 +206,12 @@ class NetworkAclAssociation(object):
class NetworkAcl(TaggedEC2Resource):
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.id = network_acl_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.associations = {}
self.default = "true" if default is True else "false"

View File

@ -1,6 +1,6 @@
import ipaddress
from moto.core import get_account_id, CloudFormationModel
from moto.core import CloudFormationModel
from .core import TaggedEC2Resource
from ..exceptions import (
DependencyViolationError,
@ -32,7 +32,7 @@ class RouteTable(TaggedEC2Resource, CloudFormationModel):
@property
def owner_id(self):
return get_account_id()
return self.ec2_backend.account_id
@staticmethod
def cloudformation_name_type():
@ -45,14 +45,14 @@ class RouteTable(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
properties = cloudformation_json["Properties"]
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)
return route_table
@ -255,7 +255,7 @@ class Route(CloudFormationModel):
@classmethod
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
@ -270,7 +270,7 @@ class Route(CloudFormationModel):
pcx_id = properties.get("VpcPeeringConnectionId")
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_id=route_table_id,
destination_cidr_block=properties.get("DestinationCidrBlock"),

View File

@ -3,7 +3,7 @@ import itertools
import json
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 ..exceptions import (
DependencyViolationError,
@ -30,6 +30,7 @@ from ..utils import (
class SecurityRule(object):
def __init__(
self,
account_id,
ip_protocol,
from_port,
to_port,
@ -37,6 +38,7 @@ class SecurityRule(object):
source_groups,
prefix_list_ids=None,
):
self.account_id = account_id
self.id = random_security_group_rule_id()
self.ip_protocol = str(ip_protocol)
self.ip_ranges = ip_ranges or []
@ -69,7 +71,7 @@ class SecurityRule(object):
@property
def owner_id(self):
return get_account_id()
return self.account_id
def __eq__(self, other):
if self.ip_protocol != other.ip_protocol:
@ -126,7 +128,7 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
self.egress_rules = []
self.enis = {}
self.vpc_id = vpc_id
self.owner_id = get_account_id()
self.owner_id = ec2_backend.account_id
self.add_tags(tags or {})
self.is_default = is_default or False
@ -135,11 +137,15 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
vpc = self.ec2_backend.vpcs.get(vpc_id)
if vpc:
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:
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
@ -181,13 +187,13 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
vpc_id = properties.get("VpcId")
security_group = ec2_backend.create_security_group(
name=resource_name,
@ -223,35 +229,44 @@ class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
@classmethod
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(
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(
new_resource_name, cloudformation_json, region_name
new_resource_name, cloudformation_json, account_id, region_name
)
@classmethod
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"]
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
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
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(
resource_name, vpc_id
)
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."""
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)
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:
@ -660,7 +681,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id)
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.
@ -748,7 +775,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id)
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:
@ -820,7 +853,13 @@ class SecurityGroupBackend:
ip_ranges.remove(item)
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.
@ -901,7 +940,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id)
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:
if (
@ -951,7 +996,13 @@ class SecurityGroupBackend:
_source_groups = self._add_source_group(source_groups, vpc_id)
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:
if (
@ -1008,7 +1059,7 @@ class SecurityGroupBackend:
_source_groups = []
for item in source_groups or []:
if "OwnerId" not in item:
item["OwnerId"] = get_account_id()
item["OwnerId"] = self.account_id
# for VPCs
if "GroupId" in item:
if not self.get_security_group_by_name_or_id(
@ -1061,13 +1112,13 @@ class SecurityGroupIngress(CloudFormationModel):
@classmethod
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
properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
group_name = properties.get("GroupName")
group_id = properties.get("GroupId")
ip_protocol = properties.get("IpProtocol")

View File

@ -306,12 +306,12 @@ class SpotFleetRequest(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
properties = cloudformation_json["Properties"]["SpotFleetRequestConfigData"]
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
spot_price = properties.get("SpotPrice")
target_capacity = properties["TargetCapacity"]

View File

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

View File

@ -1,5 +1,5 @@
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.utilities.utils import filter_resources, merge_multiple_dicts
from .core import TaggedEC2Resource
@ -38,7 +38,7 @@ class TransitGateway(TaggedEC2Resource, CloudFormationModel):
@property
def owner_id(self):
return get_account_id()
return self.ec2_backend.account_id
@staticmethod
def cloudformation_name_type():
@ -51,11 +51,11 @@ class TransitGateway(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
properties = cloudformation_json["Properties"]
description = properties["Description"]
options = dict(properties)

View File

@ -1,5 +1,4 @@
from datetime import datetime
from moto.core import get_account_id
from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.utilities.utils import merge_multiple_dicts, filter_resources
from .core import TaggedEC2Resource
@ -24,20 +23,14 @@ class TransitGatewayAttachment(TaggedEC2Resource):
self.add_tags(tags or {})
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
def create_time(self):
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):
DEFAULT_OPTIONS = {
@ -93,10 +86,6 @@ class TransitGatewayPeeringAttachment(TransitGatewayAttachment):
}
self.status = PeeringConnectionStatus()
@property
def resource_owner_id(self):
return get_account_id()
class TransitGatewayAttachmentBackend:
def __init__(self):

View File

@ -65,13 +65,13 @@ class VPCPeeringConnection(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
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"])
peer_vpc = ec2_backend.get_vpc(properties["PeerVpcId"])

View File

@ -44,7 +44,7 @@ class VPCServiceConfigurationBackend:
def elbv2_backend(self):
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):
return self.configurations.get(resource_id)

View File

@ -4,7 +4,6 @@ import weakref
from collections import defaultdict
from operator import itemgetter
from moto.core import get_account_id
from moto.core import CloudFormationModel
from .core import TaggedEC2Resource
from ..exceptions import (
@ -85,7 +84,7 @@ class VPCEndPoint(TaggedEC2Resource, CloudFormationModel):
@property
def owner_id(self):
return get_account_id()
return self.ec2_backend.account_id
@property
def physical_resource_id(self):
@ -101,7 +100,7 @@ class VPCEndPoint(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
@ -116,7 +115,7 @@ class VPCEndPoint(TaggedEC2Resource, CloudFormationModel):
route_table_ids = properties.get("RouteTableIds")
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_id=vpc_id,
service_name=service_name,
@ -167,7 +166,7 @@ class VPC(TaggedEC2Resource, CloudFormationModel):
@property
def owner_id(self):
return get_account_id()
return self.ec2_backend.account_id
@staticmethod
def cloudformation_name_type():
@ -180,13 +179,13 @@ class VPC(TaggedEC2Resource, CloudFormationModel):
@classmethod
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
properties = cloudformation_json["Properties"]
ec2_backend = ec2_backends[region_name]
ec2_backend = ec2_backends[account_id][region_name]
vpc = ec2_backend.create_vpc(
cidr_block=properties["CidrBlock"],
instance_tenancy=properties.get("InstanceTenancy", "default"),
@ -629,7 +628,7 @@ class VPCBackend:
return generic_filter(filters, vpc_end_points)
@staticmethod
def _collect_default_endpoint_services(region):
def _collect_default_endpoint_services(account_id, region):
"""Return list of default services using list of backends."""
if DEFAULT_VPC_ENDPOINT_SERVICES:
return DEFAULT_VPC_ENDPOINT_SERVICES
@ -643,7 +642,8 @@ class VPCBackend:
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:
service = _backends[region].default_vpc_endpoint_service(region, zones)
if service:
@ -757,7 +757,9 @@ class VPCBackend:
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:
if service_name not in [x["ServiceName"] for x in default_services]:
raise InvalidServiceName(service_name)

View File

@ -22,13 +22,13 @@ class VPCGatewayAttachment(CloudFormationModel):
@classmethod
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
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)
internet_gateway_id = properties.get("InternetGatewayId", None)
if vpn_gateway_id:
@ -78,14 +78,14 @@ class VpnGateway(CloudFormationModel, TaggedEC2Resource):
@classmethod
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
properties = cloudformation_json["Properties"]
_type = properties["Type"]
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)

View File

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

View File

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