MultiAccount support (#5192)
This commit is contained in:
parent
50f15b0c0b
commit
3d913f8f15
@ -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
|
||||
|
@ -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
133
docs/docs/multi_account.rst
Normal 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`.
|
||||
|
@ -35,6 +35,7 @@ Additional Resources
|
||||
docs/faq
|
||||
docs/iam
|
||||
docs/aws_config
|
||||
docs/multi_account
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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 [
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
|
@ -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],
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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",
|
||||
|
@ -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]
|
||||
|
@ -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")
|
||||
|
@ -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, ""
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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}",
|
||||
)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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():
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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")):
|
||||
|
@ -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:
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 ""
|
||||
|
@ -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 ""
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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,
|
||||
|
@ -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."""
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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"],
|
||||
|
@ -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"
|
||||
|
@ -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"),
|
||||
|
@ -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")
|
||||
|
@ -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"]
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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"])
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user