Merge pull request #25 from spulec/master

Merge upstream
This commit is contained in:
Bert Blommers 2020-01-04 09:06:23 +00:00 committed by GitHub
commit 74ad2d7d3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
132 changed files with 4351 additions and 642 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@ from .batch import mock_batch # noqa
from .cloudformation import mock_cloudformation # noqa from .cloudformation import mock_cloudformation # noqa
from .cloudformation import mock_cloudformation_deprecated # noqa from .cloudformation import mock_cloudformation_deprecated # noqa
from .cloudwatch import mock_cloudwatch, mock_cloudwatch_deprecated # noqa from .cloudwatch import mock_cloudwatch, mock_cloudwatch_deprecated # noqa
from .codecommit import mock_codecommit # noqa
from .codepipeline import mock_codepipeline # noqa
from .cognitoidentity import mock_cognitoidentity # noqa from .cognitoidentity import mock_cognitoidentity # noqa
from .cognitoidentity import mock_cognitoidentity_deprecated # noqa from .cognitoidentity import mock_cognitoidentity_deprecated # noqa
from .cognitoidp import mock_cognitoidp, mock_cognitoidp_deprecated # noqa from .cognitoidp import mock_cognitoidp, mock_cognitoidp_deprecated # noqa
@ -20,6 +22,7 @@ from .dynamodb import mock_dynamodb, mock_dynamodb_deprecated # noqa
from .dynamodb2 import mock_dynamodb2, mock_dynamodb2_deprecated # noqa from .dynamodb2 import mock_dynamodb2, mock_dynamodb2_deprecated # noqa
from .dynamodbstreams import mock_dynamodbstreams # noqa from .dynamodbstreams import mock_dynamodbstreams # noqa
from .ec2 import mock_ec2, mock_ec2_deprecated # noqa from .ec2 import mock_ec2, mock_ec2_deprecated # noqa
from .ec2_instance_connect import mock_ec2_instance_connect # noqa
from .ecr import mock_ecr, mock_ecr_deprecated # noqa from .ecr import mock_ecr, mock_ecr_deprecated # noqa
from .ecs import mock_ecs, mock_ecs_deprecated # noqa from .ecs import mock_ecs, mock_ecs_deprecated # noqa
from .elb import mock_elb, mock_elb_deprecated # noqa from .elb import mock_elb, mock_elb_deprecated # noqa

View File

@ -13,8 +13,9 @@ import cryptography.hazmat.primitives.asymmetric.rsa
from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
DEFAULT_ACCOUNT_ID = 123456789012
GOOGLE_ROOT_CA = b"""-----BEGIN CERTIFICATE----- GOOGLE_ROOT_CA = b"""-----BEGIN CERTIFICATE-----
MIIEKDCCAxCgAwIBAgIQAQAhJYiw+lmnd+8Fe2Yn3zANBgkqhkiG9w0BAQsFADBC MIIEKDCCAxCgAwIBAgIQAQAhJYiw+lmnd+8Fe2Yn3zANBgkqhkiG9w0BAQsFADBC
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS

View File

@ -101,3 +101,12 @@ class ApiKeyNotFoundException(RESTError):
super(ApiKeyNotFoundException, self).__init__( super(ApiKeyNotFoundException, self).__init__(
"NotFoundException", "Invalid API Key identifier specified" "NotFoundException", "Invalid API Key identifier specified"
) )
class ApiKeyAlreadyExists(RESTError):
code = 409
def __init__(self):
super(ApiKeyAlreadyExists, self).__init__(
"ConflictException", "API Key already exists"
)

View File

@ -32,6 +32,7 @@ from .exceptions import (
RoleNotSpecified, RoleNotSpecified,
NoIntegrationDefined, NoIntegrationDefined,
NoMethodDefined, NoMethodDefined,
ApiKeyAlreadyExists,
) )
STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}" STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}"
@ -759,6 +760,10 @@ class APIGatewayBackend(BaseBackend):
return api.delete_deployment(deployment_id) return api.delete_deployment(deployment_id)
def create_apikey(self, payload): def create_apikey(self, payload):
if payload.get("value") is not None:
for api_key in self.get_apikeys():
if api_key.get("value") == payload["value"]:
raise ApiKeyAlreadyExists()
key = ApiKey(**payload) key = ApiKey(**payload)
self.keys[key["id"]] = key self.keys[key["id"]] = key
return key return key
@ -842,3 +847,11 @@ class APIGatewayBackend(BaseBackend):
apigateway_backends = {} apigateway_backends = {}
for region_name in Session().get_available_regions("apigateway"): for region_name in Session().get_available_regions("apigateway"):
apigateway_backends[region_name] = APIGatewayBackend(region_name) apigateway_backends[region_name] = APIGatewayBackend(region_name)
for region_name in Session().get_available_regions(
"apigateway", partition_name="aws-us-gov"
):
apigateway_backends[region_name] = APIGatewayBackend(region_name)
for region_name in Session().get_available_regions(
"apigateway", partition_name="aws-cn"
):
apigateway_backends[region_name] = APIGatewayBackend(region_name)

View File

@ -9,6 +9,7 @@ from .exceptions import (
BadRequestException, BadRequestException,
CrossAccountNotAllowed, CrossAccountNotAllowed,
StageNotFoundException, StageNotFoundException,
ApiKeyAlreadyExists,
) )
@ -302,7 +303,17 @@ class APIGatewayResponse(BaseResponse):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)
if self.method == "POST": if self.method == "POST":
apikey_response = self.backend.create_apikey(json.loads(self.body)) try:
apikey_response = self.backend.create_apikey(json.loads(self.body))
except ApiKeyAlreadyExists as error:
return (
error.code,
self.headers,
'{{"message":"{0}","code":"{1}"}}'.format(
error.message, error.error_type
),
)
elif self.method == "GET": elif self.method == "GET":
apikeys_response = self.backend.get_apikeys() apikeys_response = self.backend.get_apikeys()
return 200, {}, json.dumps({"item": apikeys_response}) return 200, {}, json.dumps({"item": apikeys_response})

View File

@ -1,10 +1,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import time import time
import boto3 from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
ACCOUNT_ID = 123456789012 from moto.core import ACCOUNT_ID
class TaggableResourceMixin(object): class TaggableResourceMixin(object):
@ -77,5 +78,9 @@ class AthenaBackend(BaseBackend):
athena_backends = {} athena_backends = {}
for region in boto3.Session().get_available_regions("athena"): for region in Session().get_available_regions("athena"):
athena_backends[region] = AthenaBackend(region)
for region in Session().get_available_regions("athena", partition_name="aws-us-gov"):
athena_backends[region] = AthenaBackend(region)
for region in Session().get_available_regions("athena", partition_name="aws-cn"):
athena_backends[region] = AthenaBackend(region) athena_backends[region] = AthenaBackend(region)

View File

@ -23,7 +23,8 @@ import traceback
import weakref import weakref
import requests.adapters import requests.adapters
import boto.awslambda from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
from moto.iam.models import iam_backend from moto.iam.models import iam_backend
@ -42,11 +43,10 @@ from .utils import make_function_arn, make_function_ver_arn
from moto.sqs import sqs_backends from moto.sqs import sqs_backends
from moto.dynamodb2 import dynamodb_backends2 from moto.dynamodb2 import dynamodb_backends2
from moto.dynamodbstreams import dynamodbstreams_backends from moto.dynamodbstreams import dynamodbstreams_backends
from moto.core import ACCOUNT_ID
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ACCOUNT_ID = "123456789012"
try: try:
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
@ -1044,10 +1044,10 @@ def do_validate_s3():
return os.environ.get("VALIDATE_LAMBDA_S3", "") in ["", "1", "true"] return os.environ.get("VALIDATE_LAMBDA_S3", "") in ["", "1", "true"]
# Handle us forgotten regions, unless Lambda truly only runs out of US and lambda_backends = {}
lambda_backends = { for region in Session().get_available_regions("lambda"):
_region.name: LambdaBackend(_region.name) for _region in boto.awslambda.regions() lambda_backends[region] = LambdaBackend(region)
} for region in Session().get_available_regions("lambda", partition_name="aws-us-gov"):
lambda_backends[region] = LambdaBackend(region)
lambda_backends["ap-southeast-2"] = LambdaBackend("ap-southeast-2") for region in Session().get_available_regions("lambda", partition_name="aws-cn"):
lambda_backends["us-gov-west-1"] = LambdaBackend("us-gov-west-1") lambda_backends[region] = LambdaBackend(region)

View File

@ -8,6 +8,8 @@ from moto.awslambda import lambda_backends
from moto.batch import batch_backends from moto.batch import batch_backends
from moto.cloudformation import cloudformation_backends from moto.cloudformation import cloudformation_backends
from moto.cloudwatch import cloudwatch_backends from moto.cloudwatch import cloudwatch_backends
from moto.codecommit import codecommit_backends
from moto.codepipeline import codepipeline_backends
from moto.cognitoidentity import cognitoidentity_backends from moto.cognitoidentity import cognitoidentity_backends
from moto.cognitoidp import cognitoidp_backends from moto.cognitoidp import cognitoidp_backends
from moto.config import config_backends from moto.config import config_backends
@ -18,6 +20,7 @@ from moto.dynamodb import dynamodb_backends
from moto.dynamodb2 import dynamodb_backends2 from moto.dynamodb2 import dynamodb_backends2
from moto.dynamodbstreams import dynamodbstreams_backends from moto.dynamodbstreams import dynamodbstreams_backends
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from moto.ec2_instance_connect import ec2_instance_connect_backends
from moto.ecr import ecr_backends from moto.ecr import ecr_backends
from moto.ecs import ecs_backends from moto.ecs import ecs_backends
from moto.elb import elb_backends from moto.elb import elb_backends
@ -60,6 +63,8 @@ BACKENDS = {
"batch": batch_backends, "batch": batch_backends,
"cloudformation": cloudformation_backends, "cloudformation": cloudformation_backends,
"cloudwatch": cloudwatch_backends, "cloudwatch": cloudwatch_backends,
"codecommit": codecommit_backends,
"codepipeline": codepipeline_backends,
"cognito-identity": cognitoidentity_backends, "cognito-identity": cognitoidentity_backends,
"cognito-idp": cognitoidp_backends, "cognito-idp": cognitoidp_backends,
"config": config_backends, "config": config_backends,
@ -69,6 +74,7 @@ BACKENDS = {
"dynamodb2": dynamodb_backends2, "dynamodb2": dynamodb_backends2,
"dynamodbstreams": dynamodbstreams_backends, "dynamodbstreams": dynamodbstreams_backends,
"ec2": ec2_backends, "ec2": ec2_backends,
"ec2_instance_connect": ec2_instance_connect_backends,
"ecr": ecr_backends, "ecr": ecr_backends,
"ecs": ecs_backends, "ecs": ecs_backends,
"elb": elb_backends, "elb": elb_backends,

View File

@ -1,5 +1,4 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import boto3
import re import re
import requests.adapters import requests.adapters
from itertools import cycle from itertools import cycle
@ -12,6 +11,8 @@ import docker
import functools import functools
import threading import threading
import dateutil.parser import dateutil.parser
from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.iam import iam_backends from moto.iam import iam_backends
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
@ -28,11 +29,10 @@ from .utils import (
from moto.ec2.exceptions import InvalidSubnetIdError from moto.ec2.exceptions import InvalidSubnetIdError
from moto.ec2.models import INSTANCE_TYPES as EC2_INSTANCE_TYPES from moto.ec2.models import INSTANCE_TYPES as EC2_INSTANCE_TYPES
from moto.iam.exceptions import IAMNotFoundException from moto.iam.exceptions import IAMNotFoundException
from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
_orig_adapter_send = requests.adapters.HTTPAdapter.send _orig_adapter_send = requests.adapters.HTTPAdapter.send
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
DEFAULT_ACCOUNT_ID = 123456789012
COMPUTE_ENVIRONMENT_NAME_REGEX = re.compile( COMPUTE_ENVIRONMENT_NAME_REGEX = re.compile(
r"^[A-Za-z0-9][A-Za-z0-9_-]{1,126}[A-Za-z0-9]$" r"^[A-Za-z0-9][A-Za-z0-9_-]{1,126}[A-Za-z0-9]$"
) )
@ -182,7 +182,7 @@ class JobDefinition(BaseModel):
self._region = region_name self._region = region_name
self.container_properties = container_properties self.container_properties = container_properties
self.arn = None self.arn = None
self.status = "INACTIVE" self.status = "ACTIVE"
if parameters is None: if parameters is None:
parameters = {} parameters = {}
@ -285,7 +285,7 @@ class JobDefinition(BaseModel):
class Job(threading.Thread, BaseModel): class Job(threading.Thread, BaseModel):
def __init__(self, name, job_def, job_queue, log_backend): def __init__(self, name, job_def, job_queue, log_backend, container_overrides):
""" """
Docker Job Docker Job
@ -301,6 +301,7 @@ class Job(threading.Thread, BaseModel):
self.job_name = name self.job_name = name
self.job_id = str(uuid.uuid4()) self.job_id = str(uuid.uuid4())
self.job_definition = job_def self.job_definition = job_def
self.container_overrides = container_overrides
self.job_queue = job_queue self.job_queue = job_queue
self.job_state = "SUBMITTED" # One of SUBMITTED | PENDING | RUNNABLE | STARTING | RUNNING | SUCCEEDED | FAILED self.job_state = "SUBMITTED" # One of SUBMITTED | PENDING | RUNNABLE | STARTING | RUNNING | SUCCEEDED | FAILED
self.job_queue.jobs.append(self) self.job_queue.jobs.append(self)
@ -357,6 +358,11 @@ class Job(threading.Thread, BaseModel):
result["statusReason"] = self.job_stopped_reason result["statusReason"] = self.job_stopped_reason
return result return result
def _get_container_property(self, p, default):
return self.container_overrides.get(
p, self.job_definition.container_properties.get(p, default)
)
def run(self): def run(self):
""" """
Run the container. Run the container.
@ -375,8 +381,33 @@ class Job(threading.Thread, BaseModel):
self.job_state = "PENDING" self.job_state = "PENDING"
time.sleep(1) time.sleep(1)
image = "alpine:latest" image = self.job_definition.container_properties.get(
cmd = '/bin/sh -c "for a in `seq 1 10`; do echo Hello World; sleep 1; done"' "image", "alpine:latest"
)
privileged = self.job_definition.container_properties.get(
"privileged", False
)
cmd = self._get_container_property(
"command",
'/bin/sh -c "for a in `seq 1 10`; do echo Hello World; sleep 1; done"',
)
environment = {
e["name"]: e["value"]
for e in self._get_container_property("environment", [])
}
volumes = {
v["name"]: v["host"]
for v in self._get_container_property("volumes", [])
}
mounts = [
docker.types.Mount(
m["containerPath"],
volumes[m["sourceVolume"]]["sourcePath"],
type="bind",
read_only=m["readOnly"],
)
for m in self._get_container_property("mountPoints", [])
]
name = "{0}-{1}".format(self.job_name, self.job_id) name = "{0}-{1}".format(self.job_name, self.job_id)
self.job_state = "RUNNABLE" self.job_state = "RUNNABLE"
@ -384,8 +415,16 @@ class Job(threading.Thread, BaseModel):
time.sleep(1) time.sleep(1)
self.job_state = "STARTING" self.job_state = "STARTING"
log_config = docker.types.LogConfig(type=docker.types.LogConfig.types.JSON)
container = self.docker_client.containers.run( container = self.docker_client.containers.run(
image, cmd, detach=True, name=name image,
cmd,
detach=True,
name=name,
log_config=log_config,
environment=environment,
mounts=mounts,
privileged=privileged,
) )
self.job_state = "RUNNING" self.job_state = "RUNNING"
self.job_started_at = datetime.datetime.now() self.job_started_at = datetime.datetime.now()
@ -815,8 +854,10 @@ class BatchBackend(BaseBackend):
raise InvalidParameterValueException( raise InvalidParameterValueException(
"computeResources must contain {0}".format(param) "computeResources must contain {0}".format(param)
) )
for profile in self.iam_backend.get_instance_profiles():
if self.iam_backend.get_role_by_arn(cr["instanceRole"]) is None: if profile.arn == cr["instanceRole"]:
break
else:
raise InvalidParameterValueException( raise InvalidParameterValueException(
"could not find instanceRole {0}".format(cr["instanceRole"]) "could not find instanceRole {0}".format(cr["instanceRole"])
) )
@ -1208,7 +1249,13 @@ class BatchBackend(BaseBackend):
if queue is None: if queue is None:
raise ClientException("Job queue {0} does not exist".format(job_queue)) raise ClientException("Job queue {0} does not exist".format(job_queue))
job = Job(job_name, job_def, queue, log_backend=self.logs_backend) job = Job(
job_name,
job_def,
queue,
log_backend=self.logs_backend,
container_overrides=container_overrides,
)
self._jobs[job.job_id] = job self._jobs[job.job_id] = job
# Here comes the fun # Here comes the fun
@ -1271,7 +1318,10 @@ class BatchBackend(BaseBackend):
job.terminate(reason) job.terminate(reason)
available_regions = boto3.session.Session().get_available_regions("batch") batch_backends = {}
batch_backends = { for region in Session().get_available_regions("batch"):
region: BatchBackend(region_name=region) for region in available_regions batch_backends[region] = BatchBackend(region)
} for region in Session().get_available_regions("batch", partition_name="aws-us-gov"):
batch_backends[region] = BatchBackend(region)
for region in Session().get_available_regions("batch", partition_name="aws-cn"):
batch_backends[region] = BatchBackend(region)

View File

@ -4,7 +4,8 @@ import json
import yaml import yaml
import uuid import uuid
import boto.cloudformation from boto3 import Session
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -717,5 +718,13 @@ class CloudFormationBackend(BaseBackend):
cloudformation_backends = {} cloudformation_backends = {}
for region in boto.cloudformation.regions(): for region in Session().get_available_regions("cloudformation"):
cloudformation_backends[region.name] = CloudFormationBackend() cloudformation_backends[region] = CloudFormationBackend()
for region in Session().get_available_regions(
"cloudformation", partition_name="aws-us-gov"
):
cloudformation_backends[region] = CloudFormationBackend()
for region in Session().get_available_regions(
"cloudformation", partition_name="aws-cn"
):
cloudformation_backends[region] = CloudFormationBackend()

View File

@ -1,5 +1,4 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import collections
import functools import functools
import logging import logging
import copy import copy
@ -11,6 +10,7 @@ from moto.awslambda import models as lambda_models
from moto.batch import models as batch_models from moto.batch import models as batch_models
from moto.cloudwatch import models as cloudwatch_models from moto.cloudwatch import models as cloudwatch_models
from moto.cognitoidentity import models as cognitoidentity_models from moto.cognitoidentity import models as cognitoidentity_models
from moto.compat import collections_abc
from moto.datapipeline import models as datapipeline_models from moto.datapipeline import models as datapipeline_models
from moto.dynamodb2 import models as dynamodb2_models from moto.dynamodb2 import models as dynamodb2_models
from moto.ec2 import models as ec2_models from moto.ec2 import models as ec2_models
@ -27,6 +27,7 @@ from moto.route53 import models as route53_models
from moto.s3 import models as s3_models from moto.s3 import models as s3_models
from moto.sns import models as sns_models from moto.sns import models as sns_models
from moto.sqs import models as sqs_models from moto.sqs import models as sqs_models
from moto.core import ACCOUNT_ID
from .utils import random_suffix from .utils import random_suffix
from .exceptions import ( from .exceptions import (
ExportNotFound, ExportNotFound,
@ -404,7 +405,7 @@ def parse_output(output_logical_id, output_json, resources_map):
return output return output
class ResourceMap(collections.Mapping): class ResourceMap(collections_abc.Mapping):
""" """
This is a lazy loading map for resources. This allows us to create resources This is a lazy loading map for resources. This allows us to create resources
without needing to create a full dependency tree. Upon creation, each without needing to create a full dependency tree. Upon creation, each
@ -431,7 +432,7 @@ class ResourceMap(collections.Mapping):
# Create the default resources # Create the default resources
self._parsed_resources = { self._parsed_resources = {
"AWS::AccountId": "123456789012", "AWS::AccountId": ACCOUNT_ID,
"AWS::Region": self._region_name, "AWS::Region": self._region_name,
"AWS::StackId": stack_id, "AWS::StackId": stack_id,
"AWS::StackName": stack_name, "AWS::StackName": stack_name,
@ -633,7 +634,7 @@ class ResourceMap(collections.Mapping):
raise last_exception raise last_exception
class OutputMap(collections.Mapping): class OutputMap(collections_abc.Mapping):
def __init__(self, resources, template, stack_id): def __init__(self, resources, template, stack_id):
self._template = template self._template = template
self._stack_id = stack_id self._stack_id = stack_id

View File

@ -7,6 +7,7 @@ from six.moves.urllib.parse import urlparse
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.core.utils import amzn_request_id from moto.core.utils import amzn_request_id
from moto.s3 import s3_backend from moto.s3 import s3_backend
from moto.core import ACCOUNT_ID
from .models import cloudformation_backends from .models import cloudformation_backends
from .exceptions import ValidationError from .exceptions import ValidationError
@ -425,7 +426,9 @@ class CloudFormationResponse(BaseResponse):
stackset = self.cloudformation_backend.get_stack_set(stackset_name) stackset = self.cloudformation_backend.get_stack_set(stackset_name)
if not stackset.admin_role: if not stackset.admin_role:
stackset.admin_role = "arn:aws:iam::123456789012:role/AWSCloudFormationStackSetAdministrationRole" stackset.admin_role = "arn:aws:iam::{AccountId}:role/AWSCloudFormationStackSetAdministrationRole".format(
AccountId=ACCOUNT_ID
)
if not stackset.execution_role: if not stackset.execution_role:
stackset.execution_role = "AWSCloudFormationStackSetExecutionRole" stackset.execution_role = "AWSCloudFormationStackSetExecutionRole"
@ -1051,11 +1054,14 @@ STOP_STACK_SET_OPERATION_RESPONSE_TEMPLATE = """<StopStackSetOperationResponse x
</ResponseMetadata> </StopStackSetOperationResponse> </ResponseMetadata> </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> <DescribeStackSetOperationResult>
<StackSetOperation> <StackSetOperation>
<ExecutionRoleName>{{ stackset.execution_role }}</ExecutionRoleName> <ExecutionRoleName>{{ stackset.execution_role }}</ExecutionRoleName>
<AdministrationRoleARN>arn:aws:iam::123456789012:role/{{ stackset.admin_role }}</AdministrationRoleARN> <AdministrationRoleARN>arn:aws:iam::"""
+ ACCOUNT_ID
+ """:role/{{ stackset.admin_role }}</AdministrationRoleARN>
<StackSetId>{{ stackset.id }}</StackSetId> <StackSetId>{{ stackset.id }}</StackSetId>
<CreationTimestamp>{{ operation.CreationTimestamp }}</CreationTimestamp> <CreationTimestamp>{{ operation.CreationTimestamp }}</CreationTimestamp>
<OperationId>{{ operation.OperationId }}</OperationId> <OperationId>{{ operation.OperationId }}</OperationId>
@ -1072,15 +1078,19 @@ DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = """<DescribeStackSetOperationRes
</ResponseMetadata> </ResponseMetadata>
</DescribeStackSetOperationResponse> </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> <ListStackSetOperationResultsResult>
<Summaries> <Summaries>
{% for instance in operation.Instances %} {% for instance in operation.Instances %}
{% for account, region in instance.items() %} {% for account, region in instance.items() %}
<member> <member>
<AccountGateResult> <AccountGateResult>
<StatusReason>Function not found: arn:aws:lambda:us-west-2:123456789012:function:AWSCloudFormationStackSetAccountGate</StatusReason> <StatusReason>Function not found: arn:aws:lambda:us-west-2:"""
+ ACCOUNT_ID
+ """:function:AWSCloudFormationStackSetAccountGate</StatusReason>
<Status>SKIPPED</Status> <Status>SKIPPED</Status>
</AccountGateResult> </AccountGateResult>
<Region>{{ region }}</Region> <Region>{{ region }}</Region>
@ -1096,3 +1106,4 @@ LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = """<ListStackSetOperationRe
</ResponseMetadata> </ResponseMetadata>
</ListStackSetOperationResultsResponse> </ListStackSetOperationResultsResponse>
""" """
)

View File

@ -7,6 +7,7 @@ import os
import string import string
from cfnlint import decode, core from cfnlint import decode, core
from moto.core import ACCOUNT_ID
def generate_stack_id(stack_name, region="us-east-1", account="123456789"): def generate_stack_id(stack_name, region="us-east-1", account="123456789"):
@ -29,8 +30,8 @@ def generate_stackset_id(stackset_name):
def generate_stackset_arn(stackset_id, region_name): def generate_stackset_arn(stackset_id, region_name):
return "arn:aws:cloudformation:{}:123456789012:stackset/{}".format( return "arn:aws:cloudformation:{}:{}:stackset/{}".format(
region_name, stackset_id region_name, ACCOUNT_ID, stackset_id
) )

View File

@ -1,14 +1,16 @@
import json import json
from boto3 import Session
from moto.core.utils import iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
import boto.ec2.cloudwatch
from datetime import datetime, timedelta from datetime import datetime, timedelta
from dateutil.tz import tzutc from dateutil.tz import tzutc
from uuid import uuid4 from uuid import uuid4
from .utils import make_arn_for_dashboard from .utils import make_arn_for_dashboard
DEFAULT_ACCOUNT_ID = 123456789012 from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
_EMPTY_LIST = tuple() _EMPTY_LIST = tuple()
@ -431,5 +433,11 @@ class LogGroup(BaseModel):
cloudwatch_backends = {} cloudwatch_backends = {}
for region in boto.ec2.cloudwatch.regions(): for region in Session().get_available_regions("cloudwatch"):
cloudwatch_backends[region.name] = CloudWatchBackend() cloudwatch_backends[region] = CloudWatchBackend()
for region in Session().get_available_regions(
"cloudwatch", partition_name="aws-us-gov"
):
cloudwatch_backends[region] = CloudWatchBackend()
for region in Session().get_available_regions("cloudwatch", partition_name="aws-cn"):
cloudwatch_backends[region] = CloudWatchBackend()

View File

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

View File

@ -0,0 +1,35 @@
from moto.core.exceptions import JsonRESTError
class RepositoryNameExistsException(JsonRESTError):
code = 400
def __init__(self, repository_name):
super(RepositoryNameExistsException, self).__init__(
"RepositoryNameExistsException",
"Repository named {0} already exists".format(repository_name),
)
class RepositoryDoesNotExistException(JsonRESTError):
code = 400
def __init__(self, repository_name):
super(RepositoryDoesNotExistException, self).__init__(
"RepositoryDoesNotExistException",
"{0} does not exist".format(repository_name),
)
class InvalidRepositoryNameException(JsonRESTError):
code = 400
def __init__(self):
super(InvalidRepositoryNameException, self).__init__(
"InvalidRepositoryNameException",
"The repository name is not valid. Repository names can be any valid "
"combination of letters, numbers, "
"periods, underscores, and dashes between 1 and 100 characters in "
"length. Names are case sensitive. "
"For more information, see Limits in the AWS CodeCommit User Guide. ",
)

69
moto/codecommit/models.py Normal file
View File

@ -0,0 +1,69 @@
from boto3 import Session
from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds
from datetime import datetime
from moto.iam.models import ACCOUNT_ID
from .exceptions import RepositoryDoesNotExistException, RepositoryNameExistsException
import uuid
class CodeCommit(BaseModel):
def __init__(self, region, repository_description, repository_name):
current_date = iso_8601_datetime_with_milliseconds(datetime.utcnow())
self.repository_metadata = dict()
self.repository_metadata["repositoryName"] = repository_name
self.repository_metadata[
"cloneUrlSsh"
] = "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
region, repository_name
)
self.repository_metadata[
"cloneUrlHttp"
] = "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
region, repository_name
)
self.repository_metadata["creationDate"] = current_date
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, ACCOUNT_ID, repository_name
)
self.repository_metadata["accountId"] = ACCOUNT_ID
class CodeCommitBackend(BaseBackend):
def __init__(self):
self.repositories = {}
def create_repository(self, region, repository_name, repository_description):
repository = self.repositories.get(repository_name)
if repository:
raise RepositoryNameExistsException(repository_name)
self.repositories[repository_name] = CodeCommit(
region, repository_description, repository_name
)
return self.repositories[repository_name].repository_metadata
def get_repository(self, repository_name):
repository = self.repositories.get(repository_name)
if not repository:
raise RepositoryDoesNotExistException(repository_name)
return repository.repository_metadata
def delete_repository(self, repository_name):
repository = self.repositories.get(repository_name)
if repository:
self.repositories.pop(repository_name)
return repository.repository_metadata.get("repositoryId")
return None
codecommit_backends = {}
for region in Session().get_available_regions("codecommit"):
codecommit_backends[region] = CodeCommitBackend()

View File

@ -0,0 +1,57 @@
import json
import re
from moto.core.responses import BaseResponse
from .models import codecommit_backends
from .exceptions import InvalidRepositoryNameException
def _is_repository_name_valid(repository_name):
name_regex = re.compile(r"[\w\.-]+")
result = name_regex.split(repository_name)
if len(result) > 0:
for match in result:
if len(match) > 0:
return False
return True
class CodeCommitResponse(BaseResponse):
@property
def codecommit_backend(self):
return codecommit_backends[self.region]
def create_repository(self):
if not _is_repository_name_valid(self._get_param("repositoryName")):
raise InvalidRepositoryNameException()
repository_metadata = self.codecommit_backend.create_repository(
self.region,
self._get_param("repositoryName"),
self._get_param("repositoryDescription"),
)
return json.dumps({"repositoryMetadata": repository_metadata})
def get_repository(self):
if not _is_repository_name_valid(self._get_param("repositoryName")):
raise InvalidRepositoryNameException()
repository_metadata = self.codecommit_backend.get_repository(
self._get_param("repositoryName")
)
return json.dumps({"repositoryMetadata": repository_metadata})
def delete_repository(self):
if not _is_repository_name_valid(self._get_param("repositoryName")):
raise InvalidRepositoryNameException()
repository_id = self.codecommit_backend.delete_repository(
self._get_param("repositoryName")
)
if repository_id:
return json.dumps({"repositoryId": repository_id})
return json.dumps({})

6
moto/codecommit/urls.py Normal file
View File

@ -0,0 +1,6 @@
from __future__ import unicode_literals
from .responses import CodeCommitResponse
url_bases = ["https?://codecommit.(.+).amazonaws.com"]
url_paths = {"{0}/$": CodeCommitResponse.dispatch}

View File

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

View File

@ -0,0 +1,44 @@
from moto.core.exceptions import JsonRESTError
class InvalidStructureException(JsonRESTError):
code = 400
def __init__(self, message):
super(InvalidStructureException, self).__init__(
"InvalidStructureException", message
)
class PipelineNotFoundException(JsonRESTError):
code = 400
def __init__(self, message):
super(PipelineNotFoundException, self).__init__(
"PipelineNotFoundException", message
)
class ResourceNotFoundException(JsonRESTError):
code = 400
def __init__(self, message):
super(ResourceNotFoundException, self).__init__(
"ResourceNotFoundException", message
)
class InvalidTagsException(JsonRESTError):
code = 400
def __init__(self, message):
super(InvalidTagsException, self).__init__("InvalidTagsException", message)
class TooManyTagsException(JsonRESTError):
code = 400
def __init__(self, arn):
super(TooManyTagsException, self).__init__(
"TooManyTagsException", "Tag limit exceeded for resource [{}].".format(arn)
)

218
moto/codepipeline/models.py Normal file
View File

@ -0,0 +1,218 @@
import json
from datetime import datetime
from boto3 import Session
from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.iam.exceptions import IAMNotFoundException
from moto.iam import iam_backends
from moto.codepipeline.exceptions import (
InvalidStructureException,
PipelineNotFoundException,
ResourceNotFoundException,
InvalidTagsException,
TooManyTagsException,
)
from moto.core import BaseBackend, BaseModel
from moto.iam.models import ACCOUNT_ID
class CodePipeline(BaseModel):
def __init__(self, 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, ACCOUNT_ID, pipeline["name"]
)
self._created = datetime.utcnow()
self._updated = datetime.utcnow()
@property
def metadata(self):
return {
"pipelineArn": self._arn,
"created": iso_8601_datetime_with_milliseconds(self._created),
"updated": iso_8601_datetime_with_milliseconds(self._updated),
}
def add_default_values(self, pipeline):
for stage in pipeline["stages"]:
for action in stage["actions"]:
if "runOrder" not in action:
action["runOrder"] = 1
if "configuration" not in action:
action["configuration"] = {}
if "outputArtifacts" not in action:
action["outputArtifacts"] = []
if "inputArtifacts" not in action:
action["inputArtifacts"] = []
return pipeline
def validate_tags(self, tags):
for tag in tags:
if tag["key"].startswith("aws:"):
raise InvalidTagsException(
"Not allowed to modify system tags. "
"System tags start with 'aws:'. "
"msg=[Caller is an end user and not allowed to mutate system tags]"
)
if (len(self.tags) + len(tags)) > 50:
raise TooManyTagsException(self._arn)
class CodePipelineBackend(BaseBackend):
def __init__(self):
self.pipelines = {}
@property
def iam_backend(self):
return iam_backends["global"]
def create_pipeline(self, region, pipeline, tags):
if pipeline["name"] in self.pipelines:
raise InvalidStructureException(
"A pipeline with the name '{0}' already exists in account '{1}'".format(
pipeline["name"], ACCOUNT_ID
)
)
try:
role = self.iam_backend.get_role_by_arn(pipeline["roleArn"])
service_principal = json.loads(role.assume_role_policy_document)[
"Statement"
][0]["Principal"]["Service"]
if "codepipeline.amazonaws.com" not in service_principal:
raise IAMNotFoundException("")
except IAMNotFoundException:
raise InvalidStructureException(
"CodePipeline is not authorized to perform AssumeRole on role {}".format(
pipeline["roleArn"]
)
)
if len(pipeline["stages"]) < 2:
raise InvalidStructureException(
"Pipeline has only 1 stage(s). There should be a minimum of 2 stages in a pipeline"
)
self.pipelines[pipeline["name"]] = CodePipeline(region, pipeline)
if tags:
self.pipelines[pipeline["name"]].validate_tags(tags)
new_tags = {tag["key"]: tag["value"] for tag in tags}
self.pipelines[pipeline["name"]].tags.update(new_tags)
return pipeline, sorted(tags, key=lambda i: i["key"])
def get_pipeline(self, name):
codepipeline = self.pipelines.get(name)
if not codepipeline:
raise PipelineNotFoundException(
"Account '{0}' does not have a pipeline with name '{1}'".format(
ACCOUNT_ID, name
)
)
return codepipeline.pipeline, codepipeline.metadata
def update_pipeline(self, pipeline):
codepipeline = self.pipelines.get(pipeline["name"])
if not codepipeline:
raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format(
ACCOUNT_ID, pipeline["name"]
)
)
# version number is auto incremented
pipeline["version"] = codepipeline.pipeline["version"] + 1
codepipeline._updated = datetime.utcnow()
codepipeline.pipeline = codepipeline.add_default_values(pipeline)
return codepipeline.pipeline
def list_pipelines(self):
pipelines = []
for name, codepipeline in self.pipelines.items():
pipelines.append(
{
"name": name,
"version": codepipeline.pipeline["version"],
"created": codepipeline.metadata["created"],
"updated": codepipeline.metadata["updated"],
}
)
return sorted(pipelines, key=lambda i: i["name"])
def delete_pipeline(self, name):
self.pipelines.pop(name, None)
def list_tags_for_resource(self, arn):
name = arn.split(":")[-1]
pipeline = self.pipelines.get(name)
if not pipeline:
raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format(
ACCOUNT_ID, name
)
)
tags = [{"key": key, "value": value} for key, value in pipeline.tags.items()]
return sorted(tags, key=lambda i: i["key"])
def tag_resource(self, arn, tags):
name = arn.split(":")[-1]
pipeline = self.pipelines.get(name)
if not pipeline:
raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format(
ACCOUNT_ID, name
)
)
pipeline.validate_tags(tags)
for tag in tags:
pipeline.tags.update({tag["key"]: tag["value"]})
def untag_resource(self, arn, tag_keys):
name = arn.split(":")[-1]
pipeline = self.pipelines.get(name)
if not pipeline:
raise ResourceNotFoundException(
"The account with id '{0}' does not include a pipeline with the name '{1}'".format(
ACCOUNT_ID, name
)
)
for key in tag_keys:
pipeline.tags.pop(key, None)
codepipeline_backends = {}
for region in Session().get_available_regions("codepipeline"):
codepipeline_backends[region] = CodePipelineBackend()
for region in Session().get_available_regions(
"codepipeline", partition_name="aws-us-gov"
):
codepipeline_backends[region] = CodePipelineBackend()
for region in Session().get_available_regions("codepipeline", partition_name="aws-cn"):
codepipeline_backends[region] = CodePipelineBackend()

View File

@ -0,0 +1,62 @@
import json
from moto.core.responses import BaseResponse
from .models import codepipeline_backends
class CodePipelineResponse(BaseResponse):
@property
def codepipeline_backend(self):
return codepipeline_backends[self.region]
def create_pipeline(self):
pipeline, tags = self.codepipeline_backend.create_pipeline(
self.region, self._get_param("pipeline"), self._get_param("tags")
)
return json.dumps({"pipeline": pipeline, "tags": tags})
def get_pipeline(self):
pipeline, metadata = self.codepipeline_backend.get_pipeline(
self._get_param("name")
)
return json.dumps({"pipeline": pipeline, "metadata": metadata})
def update_pipeline(self):
pipeline = self.codepipeline_backend.update_pipeline(
self._get_param("pipeline")
)
return json.dumps({"pipeline": pipeline})
def list_pipelines(self):
pipelines = self.codepipeline_backend.list_pipelines()
return json.dumps({"pipelines": pipelines})
def delete_pipeline(self):
self.codepipeline_backend.delete_pipeline(self._get_param("name"))
return ""
def list_tags_for_resource(self):
tags = self.codepipeline_backend.list_tags_for_resource(
self._get_param("resourceArn")
)
return json.dumps({"tags": tags})
def tag_resource(self):
self.codepipeline_backend.tag_resource(
self._get_param("resourceArn"), self._get_param("tags")
)
return ""
def untag_resource(self):
self.codepipeline_backend.untag_resource(
self._get_param("resourceArn"), self._get_param("tagKeys")
)
return ""

View File

@ -0,0 +1,6 @@
from __future__ import unicode_literals
from .responses import CodePipelineResponse
url_bases = ["https?://codepipeline.(.+).amazonaws.com"]
url_paths = {"{0}/$": CodePipelineResponse.dispatch}

View File

@ -3,7 +3,7 @@ from __future__ import unicode_literals
import datetime import datetime
import json import json
import boto.cognito.identity from boto3 import Session
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -136,5 +136,13 @@ class CognitoIdentityBackend(BaseBackend):
cognitoidentity_backends = {} cognitoidentity_backends = {}
for region in boto.cognito.identity.regions(): for region in Session().get_available_regions("cognito-identity"):
cognitoidentity_backends[region.name] = CognitoIdentityBackend(region.name) cognitoidentity_backends[region] = CognitoIdentityBackend(region)
for region in Session().get_available_regions(
"cognito-identity", partition_name="aws-us-gov"
):
cognitoidentity_backends[region] = CognitoIdentityBackend(region)
for region in Session().get_available_regions(
"cognito-identity", partition_name="aws-cn"
):
cognitoidentity_backends[region] = CognitoIdentityBackend(region)

View File

@ -9,7 +9,7 @@ import os
import time import time
import uuid import uuid
import boto.cognito.identity from boto3 import Session
from jose import jws from jose import jws
from moto.compat import OrderedDict from moto.compat import OrderedDict
@ -749,8 +749,14 @@ class CognitoIdpBackend(BaseBackend):
cognitoidp_backends = {} cognitoidp_backends = {}
for region in boto.cognito.identity.regions(): for region in Session().get_available_regions("cognito-idp"):
cognitoidp_backends[region.name] = CognitoIdpBackend(region.name) cognitoidp_backends[region] = CognitoIdpBackend(region)
for region in Session().get_available_regions(
"cognito-idp", partition_name="aws-us-gov"
):
cognitoidp_backends[region] = CognitoIdpBackend(region)
for region in Session().get_available_regions("cognito-idp", partition_name="aws-cn"):
cognitoidp_backends[region] = CognitoIdpBackend(region)
# Hack to help moto-server process requests on localhost, where the region isn't # Hack to help moto-server process requests on localhost, where the region isn't

View File

@ -3,3 +3,8 @@ try:
except ImportError: except ImportError:
# python 2.6 or earlier, use backport # python 2.6 or earlier, use backport
from ordereddict import OrderedDict # noqa from ordereddict import OrderedDict # noqa
try:
import collections.abc as collections_abc # noqa
except ImportError:
import collections as collections_abc # noqa

View File

@ -45,7 +45,8 @@ from moto.config.exceptions import (
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.s3.config import s3_config_query from moto.s3.config import s3_config_query
DEFAULT_ACCOUNT_ID = "123456789012" from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
POP_STRINGS = [ POP_STRINGS = [
"capitalizeStart", "capitalizeStart",
"CapitalizeStart", "CapitalizeStart",
@ -1083,6 +1084,9 @@ class ConfigBackend(BaseBackend):
config_backends = {} config_backends = {}
boto3_session = Session() for region in Session().get_available_regions("config"):
for region in boto3_session.get_available_regions("config"): config_backends[region] = ConfigBackend()
for region in Session().get_available_regions("config", partition_name="aws-us-gov"):
config_backends[region] = ConfigBackend()
for region in Session().get_available_regions("config", partition_name="aws-cn"):
config_backends[region] = ConfigBackend() config_backends[region] = ConfigBackend()

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from .models import BaseModel, BaseBackend, moto_api_backend # noqa from .models import BaseModel, BaseBackend, moto_api_backend, ACCOUNT_ID # noqa
from .responses import ActionAuthenticatorMixin from .responses import ActionAuthenticatorMixin
moto_api_backends = {"global": moto_api_backend} moto_api_backends = {"global": moto_api_backend}

View File

@ -24,7 +24,8 @@ from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials from botocore.credentials import Credentials
from six import string_types from six import string_types
from moto.iam.models import ACCOUNT_ID, Policy from moto.core import ACCOUNT_ID
from moto.iam.models import Policy
from moto.iam import iam_backend from moto.iam import iam_backend
from moto.core.exceptions import ( from moto.core.exceptions import (
SignatureDoesNotMatchError, SignatureDoesNotMatchError,

View File

@ -23,6 +23,9 @@ from .utils import (
) )
ACCOUNT_ID = os.environ.get("MOTO_ACCOUNT_ID", "123456789012")
class BaseMockAWS(object): class BaseMockAWS(object):
nested_count = 0 nested_count = 0

View File

@ -304,3 +304,27 @@ def path_url(url):
if parsed_url.query: if parsed_url.query:
path = path + "?" + parsed_url.query path = path + "?" + parsed_url.query
return path return path
def py2_strip_unicode_keys(blob):
"""For Python 2 Only -- this will convert unicode keys in nested Dicts, Lists, and Sets to standard strings."""
if type(blob) == unicode: # noqa
return str(blob)
elif type(blob) == dict:
for key in list(blob.keys()):
value = blob.pop(key)
blob[str(key)] = py2_strip_unicode_keys(value)
elif type(blob) == list:
for i in range(0, len(blob)):
blob[i] = py2_strip_unicode_keys(blob[i])
elif type(blob) == set:
new_set = set()
for value in blob:
new_set.add(py2_strip_unicode_keys(value))
blob = new_set
return blob

View File

@ -1,7 +1,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime
import boto.datapipeline from boto3 import Session
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys
@ -142,5 +143,11 @@ class DataPipelineBackend(BaseBackend):
datapipeline_backends = {} datapipeline_backends = {}
for region in boto.datapipeline.regions(): for region in Session().get_available_regions("datapipeline"):
datapipeline_backends[region.name] = DataPipelineBackend() datapipeline_backends[region] = DataPipelineBackend()
for region in Session().get_available_regions(
"datapipeline", partition_name="aws-us-gov"
):
datapipeline_backends[region] = DataPipelineBackend()
for region in Session().get_available_regions("datapipeline", partition_name="aws-cn"):
datapipeline_backends[region] = DataPipelineBackend(region)

View File

@ -1,5 +1,5 @@
import collections
import six import six
from moto.compat import collections_abc
from moto.core.utils import get_random_hex from moto.core.utils import get_random_hex
@ -8,13 +8,13 @@ def get_random_pipeline_id():
def remove_capitalization_of_dict_keys(obj): def remove_capitalization_of_dict_keys(obj):
if isinstance(obj, collections.Mapping): if isinstance(obj, collections_abc.Mapping):
result = obj.__class__() result = obj.__class__()
for key, value in obj.items(): for key, value in obj.items():
normalized_key = key[:1].lower() + key[1:] normalized_key = key[:1].lower() + key[1:]
result[normalized_key] = remove_capitalization_of_dict_keys(value) result[normalized_key] = remove_capitalization_of_dict_keys(value)
return result return result
elif isinstance(obj, collections.Iterable) and not isinstance( elif isinstance(obj, collections_abc.Iterable) and not isinstance(
obj, six.string_types obj, six.string_types
): ):
result = obj.__class__() result = obj.__class__()

View File

@ -1,4 +1,5 @@
import boto3 from boto3 import Session
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -226,5 +227,9 @@ class DataSyncBackend(BaseBackend):
datasync_backends = {} datasync_backends = {}
for region in boto3.Session().get_available_regions("datasync"): for region in Session().get_available_regions("datasync"):
datasync_backends[region] = DataSyncBackend(region_name=region) datasync_backends[region] = DataSyncBackend(region)
for region in Session().get_available_regions("datasync", partition_name="aws-us-gov"):
datasync_backends[region] = DataSyncBackend(region)
for region in Session().get_available_regions("datasync", partition_name="aws-cn"):
datasync_backends[region] = DataSyncBackend(region)

View File

@ -6,6 +6,7 @@ import json
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
from moto.core import ACCOUNT_ID
from .comparisons import get_comparison_func from .comparisons import get_comparison_func
@ -277,8 +278,8 @@ class Table(BaseModel):
if attribute_name == "StreamArn": if attribute_name == "StreamArn":
region = "us-east-1" region = "us-east-1"
time = "2000-01-01T00:00:00.000" time = "2000-01-01T00:00:00.000"
return "arn:aws:dynamodb:{0}:123456789012:table/{1}/stream/{2}".format( return "arn:aws:dynamodb:{0}:{1}:table/{2}/stream/{3}".format(
region, self.name, time region, ACCOUNT_ID, self.name, time
) )
raise UnformattedGetAttTemplateException() raise UnformattedGetAttTemplateException()

View File

@ -8,7 +8,7 @@ import re
import uuid import uuid
import six import six
import boto3 from boto3 import Session
from botocore.exceptions import ParamValidationError from botocore.exceptions import ParamValidationError
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -586,7 +586,9 @@ class StreamRecord(BaseModel):
self.record["dynamodb"]["OldImage"] = old_a self.record["dynamodb"]["OldImage"] = old_a
# This is a substantial overestimate but it's the easiest to do now # This is a substantial overestimate but it's the easiest to do now
self.record["dynamodb"]["SizeBytes"] = len(json.dumps(self.record["dynamodb"])) self.record["dynamodb"]["SizeBytes"] = len(
dynamo_json_dump(self.record["dynamodb"])
)
def to_json(self): def to_json(self):
return self.record return self.record
@ -1484,7 +1486,10 @@ class DynamoDBBackend(BaseBackend):
return table.ttl return table.ttl
available_regions = boto3.session.Session().get_available_regions("dynamodb") dynamodb_backends = {}
dynamodb_backends = { for region in Session().get_available_regions("dynamodb"):
region: DynamoDBBackend(region_name=region) for region in available_regions dynamodb_backends[region] = DynamoDBBackend(region)
} for region in Session().get_available_regions("dynamodb", partition_name="aws-us-gov"):
dynamodb_backends[region] = DynamoDBBackend(region)
for region in Session().get_available_regions("dynamodb", partition_name="aws-cn"):
dynamodb_backends[region] = DynamoDBBackend(region)

View File

@ -438,9 +438,12 @@ class DynamoHandler(BaseResponse):
all_indexes = (table.global_indexes or []) + (table.indexes or []) all_indexes = (table.global_indexes or []) + (table.indexes or [])
indexes_by_name = dict((i["IndexName"], i) for i in all_indexes) indexes_by_name = dict((i["IndexName"], i) for i in all_indexes)
if index_name not in indexes_by_name: if index_name not in indexes_by_name:
raise ValueError( er = "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException"
"Invalid index: %s for table: %s. Available indexes are: %s" return self.error(
% (index_name, name, ", ".join(indexes_by_name.keys())) er,
"Invalid index: {} for table: {}. Available indexes are: {}".format(
index_name, name, ", ".join(indexes_by_name.keys())
),
) )
index = indexes_by_name[index_name]["KeySchema"] index = indexes_by_name[index_name]["KeySchema"]
@ -481,7 +484,9 @@ class DynamoHandler(BaseResponse):
] ]
elif "begins_with" in range_key_expression: elif "begins_with" in range_key_expression:
range_comparison = "BEGINS_WITH" range_comparison = "BEGINS_WITH"
range_values = [value_alias_map[range_key_expression_components[1]]] range_values = [
value_alias_map[range_key_expression_components[-1]]
]
else: else:
range_values = [value_alias_map[range_key_expression_components[2]]] range_values = [value_alias_map[range_key_expression_components[2]]]
else: else:

View File

@ -2,9 +2,10 @@ from __future__ import unicode_literals
import os import os
import json import json
import boto3
import base64 import base64
from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.dynamodb2.models import dynamodb_backends from moto.dynamodb2.models import dynamodb_backends
@ -139,7 +140,14 @@ class DynamoDBStreamsBackend(BaseBackend):
return json.dumps(shard_iterator.get(limit)) return json.dumps(shard_iterator.get(limit))
available_regions = boto3.session.Session().get_available_regions("dynamodbstreams") dynamodbstreams_backends = {}
dynamodbstreams_backends = { for region in Session().get_available_regions("dynamodbstreams"):
region: DynamoDBStreamsBackend(region=region) for region in available_regions dynamodbstreams_backends[region] = DynamoDBStreamsBackend(region)
} for region in Session().get_available_regions(
"dynamodbstreams", partition_name="aws-us-gov"
):
dynamodbstreams_backends[region] = DynamoDBStreamsBackend(region)
for region in Session().get_available_regions(
"dynamodbstreams", partition_name="aws-cn"
):
dynamodbstreams_backends[region] = DynamoDBStreamsBackend(region)

View File

@ -8,9 +8,9 @@ import os
import re import re
import six import six
import warnings import warnings
from pkg_resources import resource_filename
import boto.ec2 from boto3 import Session
from pkg_resources import resource_filename
from collections import defaultdict from collections import defaultdict
import weakref import weakref
@ -1473,7 +1473,13 @@ class Zone(object):
class RegionsAndZonesBackend(object): class RegionsAndZonesBackend(object):
regions = [Region(ri.name, ri.endpoint) for ri in boto.ec2.regions()] regions = []
for region in Session().get_available_regions("ec2"):
regions.append(Region(region, "ec2.{}.amazonaws.com".format(region)))
for region in Session().get_available_regions("ec2", partition_name="aws-us-gov"):
regions.append(Region(region, "ec2.{}.amazonaws.com".format(region)))
for region in Session().get_available_regions("ec2", partition_name="aws-cn"):
regions.append(Region(region, "ec2.{}.amazonaws.com.cn".format(region)))
zones = { zones = {
"ap-south-1": [ "ap-south-1": [
@ -1536,6 +1542,11 @@ class RegionsAndZonesBackend(object):
zone_id="apne1-az2", zone_id="apne1-az2",
), ),
], ],
"ap-east-1": [
Zone(region_name="ap-east-1", name="ap-east-1a", zone_id="ape1-az1"),
Zone(region_name="ap-east-1", name="ap-east-1b", zone_id="ape1-az2"),
Zone(region_name="ap-east-1", name="ap-east-1c", zone_id="ape1-az3"),
],
"sa-east-1": [ "sa-east-1": [
Zone(region_name="sa-east-1", name="sa-east-1a", zone_id="sae1-az1"), Zone(region_name="sa-east-1", name="sa-east-1a", zone_id="sae1-az1"),
Zone(region_name="sa-east-1", name="sa-east-1c", zone_id="sae1-az3"), Zone(region_name="sa-east-1", name="sa-east-1c", zone_id="sae1-az3"),
@ -1605,10 +1616,32 @@ class RegionsAndZonesBackend(object):
Zone(region_name="us-west-2", name="us-west-2b", zone_id="usw2-az1"), Zone(region_name="us-west-2", name="us-west-2b", zone_id="usw2-az1"),
Zone(region_name="us-west-2", name="us-west-2c", zone_id="usw2-az3"), Zone(region_name="us-west-2", name="us-west-2c", zone_id="usw2-az3"),
], ],
"me-south-1": [
Zone(region_name="me-south-1", name="me-south-1a", zone_id="mes1-az1"),
Zone(region_name="me-south-1", name="me-south-1b", zone_id="mes1-az2"),
Zone(region_name="me-south-1", name="me-south-1c", zone_id="mes1-az3"),
],
"cn-north-1": [ "cn-north-1": [
Zone(region_name="cn-north-1", name="cn-north-1a", zone_id="cnn1-az1"), Zone(region_name="cn-north-1", name="cn-north-1a", zone_id="cnn1-az1"),
Zone(region_name="cn-north-1", name="cn-north-1b", zone_id="cnn1-az2"), Zone(region_name="cn-north-1", name="cn-north-1b", zone_id="cnn1-az2"),
], ],
"cn-northwest-1": [
Zone(
region_name="cn-northwest-1",
name="cn-northwest-1a",
zone_id="cnnw1-az1",
),
Zone(
region_name="cn-northwest-1",
name="cn-northwest-1b",
zone_id="cnnw1-az2",
),
Zone(
region_name="cn-northwest-1",
name="cn-northwest-1c",
zone_id="cnnw1-az3",
),
],
"us-gov-west-1": [ "us-gov-west-1": [
Zone( Zone(
region_name="us-gov-west-1", name="us-gov-west-1a", zone_id="usgw1-az1" region_name="us-gov-west-1", name="us-gov-west-1a", zone_id="usgw1-az1"
@ -1620,6 +1653,17 @@ class RegionsAndZonesBackend(object):
region_name="us-gov-west-1", name="us-gov-west-1c", zone_id="usgw1-az3" region_name="us-gov-west-1", name="us-gov-west-1c", zone_id="usgw1-az3"
), ),
], ],
"us-gov-east-1": [
Zone(
region_name="us-gov-east-1", name="us-gov-east-1a", zone_id="usge1-az1"
),
Zone(
region_name="us-gov-east-1", name="us-gov-east-1b", zone_id="usge1-az2"
),
Zone(
region_name="us-gov-east-1", name="us-gov-east-1c", zone_id="usge1-az3"
),
],
} }
def describe_regions(self, region_names=[]): def describe_regions(self, region_names=[]):
@ -2449,6 +2493,7 @@ class VPC(TaggedEC2Resource):
self.is_default = "true" if is_default else "false" self.is_default = "true" if is_default else "false"
self.enable_dns_support = "true" self.enable_dns_support = "true"
self.classic_link_enabled = "false" self.classic_link_enabled = "false"
self.classic_link_dns_supported = "false"
# This attribute is set to 'true' only for default VPCs # This attribute is set to 'true' only for default VPCs
# or VPCs created using the wizard of the VPC console # or VPCs created using the wizard of the VPC console
self.enable_dns_hostnames = "true" if is_default else "false" self.enable_dns_hostnames = "true" if is_default else "false"
@ -3306,6 +3351,7 @@ class Route(object):
local=False, local=False,
gateway=None, gateway=None,
instance=None, instance=None,
nat_gateway=None,
interface=None, interface=None,
vpc_pcx=None, vpc_pcx=None,
): ):
@ -3315,6 +3361,7 @@ class Route(object):
self.local = local self.local = local
self.gateway = gateway self.gateway = gateway
self.instance = instance self.instance = instance
self.nat_gateway = nat_gateway
self.interface = interface self.interface = interface
self.vpc_pcx = vpc_pcx self.vpc_pcx = vpc_pcx
@ -3327,6 +3374,7 @@ class Route(object):
gateway_id = properties.get("GatewayId") gateway_id = properties.get("GatewayId")
instance_id = properties.get("InstanceId") instance_id = properties.get("InstanceId")
interface_id = properties.get("NetworkInterfaceId") interface_id = properties.get("NetworkInterfaceId")
nat_gateway_id = properties.get("NatGatewayId")
pcx_id = properties.get("VpcPeeringConnectionId") pcx_id = properties.get("VpcPeeringConnectionId")
route_table_id = properties["RouteTableId"] route_table_id = properties["RouteTableId"]
@ -3336,6 +3384,7 @@ class Route(object):
destination_cidr_block=properties.get("DestinationCidrBlock"), destination_cidr_block=properties.get("DestinationCidrBlock"),
gateway_id=gateway_id, gateway_id=gateway_id,
instance_id=instance_id, instance_id=instance_id,
nat_gateway_id=nat_gateway_id,
interface_id=interface_id, interface_id=interface_id,
vpc_peering_connection_id=pcx_id, vpc_peering_connection_id=pcx_id,
) )
@ -3353,6 +3402,7 @@ class RouteBackend(object):
local=False, local=False,
gateway_id=None, gateway_id=None,
instance_id=None, instance_id=None,
nat_gateway_id=None,
interface_id=None, interface_id=None,
vpc_peering_connection_id=None, vpc_peering_connection_id=None,
): ):
@ -3373,12 +3423,17 @@ class RouteBackend(object):
except ValueError: except ValueError:
raise InvalidDestinationCIDRBlockParameterError(destination_cidr_block) raise InvalidDestinationCIDRBlockParameterError(destination_cidr_block)
nat_gateway = None
if nat_gateway_id is not None:
nat_gateway = self.nat_gateways.get(nat_gateway_id)
route = Route( route = Route(
route_table, route_table,
destination_cidr_block, destination_cidr_block,
local=local, local=local,
gateway=gateway, gateway=gateway,
instance=self.get_instance(instance_id) if instance_id else None, instance=self.get_instance(instance_id) if instance_id else None,
nat_gateway=nat_gateway,
interface=None, interface=None,
vpc_pcx=self.get_vpc_peering_connection(vpc_peering_connection_id) vpc_pcx=self.get_vpc_peering_connection(vpc_peering_connection_id)
if vpc_peering_connection_id if vpc_peering_connection_id
@ -4827,7 +4882,35 @@ class NatGatewayBackend(object):
super(NatGatewayBackend, self).__init__() super(NatGatewayBackend, self).__init__()
def get_all_nat_gateways(self, filters): def get_all_nat_gateways(self, filters):
return self.nat_gateways.values() nat_gateways = self.nat_gateways.values()
if filters is not None:
if filters.get("nat-gateway-id") is not None:
nat_gateways = [
nat_gateway
for nat_gateway in nat_gateways
if nat_gateway.id in filters["nat-gateway-id"]
]
if filters.get("vpc-id") is not None:
nat_gateways = [
nat_gateway
for nat_gateway in nat_gateways
if nat_gateway.vpc_id in filters["vpc-id"]
]
if filters.get("subnet-id") is not None:
nat_gateways = [
nat_gateway
for nat_gateway in nat_gateways
if nat_gateway.subnet_id in filters["subnet-id"]
]
if filters.get("state") is not None:
nat_gateways = [
nat_gateway
for nat_gateway in nat_gateways
if nat_gateway.state in filters["state"]
]
return nat_gateways
def create_nat_gateway(self, subnet_id, allocation_id): def create_nat_gateway(self, subnet_id, allocation_id):
nat_gateway = NatGateway(self, subnet_id, allocation_id) nat_gateway = NatGateway(self, subnet_id, allocation_id)

View File

@ -6,6 +6,7 @@ from moto.core.responses import BaseResponse
from moto.core.utils import camelcase_to_underscores from moto.core.utils import camelcase_to_underscores
from moto.ec2.utils import filters_from_querystring, dict_from_querystring from moto.ec2.utils import filters_from_querystring, dict_from_querystring
from moto.elbv2 import elbv2_backends from moto.elbv2 import elbv2_backends
from moto.core import ACCOUNT_ID
class InstanceResponse(BaseResponse): class InstanceResponse(BaseResponse):
@ -246,10 +247,13 @@ class InstanceResponse(BaseResponse):
return EC2_MODIFY_INSTANCE_ATTRIBUTE return EC2_MODIFY_INSTANCE_ATTRIBUTE
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> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<reservationId>{{ reservation.id }}</reservationId> <reservationId>{{ reservation.id }}</reservationId>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<groupSet> <groupSet>
<item> <item>
<groupId>sg-245f6a01</groupId> <groupId>sg-245f6a01</groupId>
@ -331,7 +335,9 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
<vpcId>{{ nic.subnet.vpc_id }}</vpcId> <vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% endif %} {% endif %}
<description>Primary network interface</description> <description>Primary network interface</description>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<status>in-use</status> <status>in-use</status>
<macAddress>1b:2b:3c:4d:5e:6f</macAddress> <macAddress>1b:2b:3c:4d:5e:6f</macAddress>
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress> <privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
@ -354,7 +360,9 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>123456789012</ipOwnerId> <ipOwnerId>"""
+ ACCOUNT_ID
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
<privateIpAddressesSet> <privateIpAddressesSet>
@ -364,7 +372,9 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>123456789012</ipOwnerId> <ipOwnerId>"""
+ ACCOUNT_ID
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
</item> </item>
@ -376,14 +386,18 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
{% endfor %} {% endfor %}
</instancesSet> </instancesSet>
</RunInstancesResponse>""" </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> <requestId>fdcdcab1-ae5c-489e-9c33-4637c5dda355</requestId>
<reservationSet> <reservationSet>
{% for reservation in reservations %} {% for reservation in reservations %}
<item> <item>
<reservationId>{{ reservation.id }}</reservationId> <reservationId>{{ reservation.id }}</reservationId>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<groupSet> <groupSet>
{% for group in reservation.dynamic_group_list %} {% for group in reservation.dynamic_group_list %}
<item> <item>
@ -476,7 +490,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
{% endfor %} {% endfor %}
</blockDeviceMapping> </blockDeviceMapping>
<virtualizationType>{{ instance.virtualization_type }}</virtualizationType> <virtualizationType>{{ instance.virtualization_type }}</virtualizationType>
<clientToken>ABCDE1234567890123</clientToken> <clientToken>ABCDE"""
+ ACCOUNT_ID
+ """3</clientToken>
{% if instance.get_tags() %} {% if instance.get_tags() %}
<tagSet> <tagSet>
{% for tag in instance.get_tags() %} {% for tag in instance.get_tags() %}
@ -499,7 +515,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
<vpcId>{{ nic.subnet.vpc_id }}</vpcId> <vpcId>{{ nic.subnet.vpc_id }}</vpcId>
{% endif %} {% endif %}
<description>Primary network interface</description> <description>Primary network interface</description>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<status>in-use</status> <status>in-use</status>
<macAddress>1b:2b:3c:4d:5e:6f</macAddress> <macAddress>1b:2b:3c:4d:5e:6f</macAddress>
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress> <privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
@ -526,7 +544,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>123456789012</ipOwnerId> <ipOwnerId>"""
+ ACCOUNT_ID
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
<privateIpAddressesSet> <privateIpAddressesSet>
@ -536,7 +556,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
{% if nic.public_ip %} {% if nic.public_ip %}
<association> <association>
<publicIp>{{ nic.public_ip }}</publicIp> <publicIp>{{ nic.public_ip }}</publicIp>
<ipOwnerId>123456789012</ipOwnerId> <ipOwnerId>"""
+ ACCOUNT_ID
+ """</ipOwnerId>
</association> </association>
{% endif %} {% endif %}
</item> </item>
@ -554,6 +576,7 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
<nextToken>{{ next_token }}</nextToken> <nextToken>{{ next_token }}</nextToken>
{% endif %} {% endif %}
</DescribeInstancesResponse>""" </DescribeInstancesResponse>"""
)
EC2_TERMINATE_INSTANCES = """ EC2_TERMINATE_INSTANCES = """
<TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> <TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">

View File

@ -18,6 +18,7 @@ class RouteTables(BaseResponse):
destination_cidr_block = self._get_param("DestinationCidrBlock") destination_cidr_block = self._get_param("DestinationCidrBlock")
gateway_id = self._get_param("GatewayId") gateway_id = self._get_param("GatewayId")
instance_id = self._get_param("InstanceId") instance_id = self._get_param("InstanceId")
nat_gateway_id = self._get_param("NatGatewayId")
interface_id = self._get_param("NetworkInterfaceId") interface_id = self._get_param("NetworkInterfaceId")
pcx_id = self._get_param("VpcPeeringConnectionId") pcx_id = self._get_param("VpcPeeringConnectionId")
@ -26,6 +27,7 @@ class RouteTables(BaseResponse):
destination_cidr_block, destination_cidr_block,
gateway_id=gateway_id, gateway_id=gateway_id,
instance_id=instance_id, instance_id=instance_id,
nat_gateway_id=nat_gateway_id,
interface_id=interface_id, interface_id=interface_id,
vpc_peering_connection_id=pcx_id, vpc_peering_connection_id=pcx_id,
) )
@ -173,6 +175,10 @@ DESCRIBE_ROUTE_TABLES_RESPONSE = """
<origin>CreateRoute</origin> <origin>CreateRoute</origin>
<state>blackhole</state> <state>blackhole</state>
{% endif %} {% endif %}
{% if route.nat_gateway %}
<natGatewayId>{{ route.nat_gateway.id }}</natGatewayId>
<state>active</state>
{% endif %}
</item> </item>
{% endfor %} {% endfor %}
</routeSet> </routeSet>

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.ec2.utils import filters_from_querystring from moto.ec2.utils import filters_from_querystring
from moto.core import ACCOUNT_ID
def try_parse_int(value, default=None): def try_parse_int(value, default=None):
@ -171,12 +172,15 @@ DELETE_GROUP_RESPONSE = """<DeleteSecurityGroupResponse xmlns="http://ec2.amazon
<return>true</return> <return>true</return>
</DeleteSecurityGroupResponse>""" </DeleteSecurityGroupResponse>"""
DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> DESCRIBE_SECURITY_GROUPS_RESPONSE = (
"""<DescribeSecurityGroupsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<securityGroupInfo> <securityGroupInfo>
{% for group in groups %} {% for group in groups %}
<item> <item>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<groupId>{{ group.id }}</groupId> <groupId>{{ group.id }}</groupId>
<groupName>{{ group.name }}</groupName> <groupName>{{ group.name }}</groupName>
<groupDescription>{{ group.description }}</groupDescription> <groupDescription>{{ group.description }}</groupDescription>
@ -196,7 +200,9 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
<groups> <groups>
{% for source_group in rule.source_groups %} {% for source_group in rule.source_groups %}
<item> <item>
<userId>123456789012</userId> <userId>"""
+ ACCOUNT_ID
+ """</userId>
<groupId>{{ source_group.id }}</groupId> <groupId>{{ source_group.id }}</groupId>
<groupName>{{ source_group.name }}</groupName> <groupName>{{ source_group.name }}</groupName>
</item> </item>
@ -225,7 +231,9 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
<groups> <groups>
{% for source_group in rule.source_groups %} {% for source_group in rule.source_groups %}
<item> <item>
<userId>123456789012</userId> <userId>"""
+ ACCOUNT_ID
+ """</userId>
<groupId>{{ source_group.id }}</groupId> <groupId>{{ source_group.id }}</groupId>
<groupName>{{ source_group.name }}</groupName> <groupName>{{ source_group.name }}</groupName>
</item> </item>
@ -255,6 +263,7 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
{% endfor %} {% endfor %}
</securityGroupInfo> </securityGroupInfo>
</DescribeSecurityGroupsResponse>""" </DescribeSecurityGroupsResponse>"""
)
AUTHORIZE_SECURITY_GROUP_INGRESS_REPONSE = """<AuthorizeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> AUTHORIZE_SECURITY_GROUP_INGRESS_REPONSE = """<AuthorizeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.core import ACCOUNT_ID
class VPCPeeringConnections(BaseResponse): class VPCPeeringConnections(BaseResponse):
@ -40,7 +41,8 @@ class VPCPeeringConnections(BaseResponse):
return template.render() return template.render()
CREATE_VPC_PEERING_CONNECTION_RESPONSE = """ CREATE_VPC_PEERING_CONNECTION_RESPONSE = (
"""
<CreateVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/"> <CreateVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId> <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<vpcPeeringConnection> <vpcPeeringConnection>
@ -56,7 +58,9 @@ CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
</peeringOptions> </peeringOptions>
</requesterVpcInfo> </requesterVpcInfo>
<accepterVpcInfo> <accepterVpcInfo>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId> <vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
</accepterVpcInfo> </accepterVpcInfo>
<status> <status>
@ -68,8 +72,10 @@ CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
</vpcPeeringConnection> </vpcPeeringConnection>
</CreateVpcPeeringConnectionResponse> </CreateVpcPeeringConnectionResponse>
""" """
)
DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """ DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = (
"""
<DescribeVpcPeeringConnectionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/"> <DescribeVpcPeeringConnectionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId> <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<vpcPeeringConnectionSet> <vpcPeeringConnectionSet>
@ -82,7 +88,9 @@ DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock> <cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
</requesterVpcInfo> </requesterVpcInfo>
<accepterVpcInfo> <accepterVpcInfo>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId> <vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
<cidrBlock>{{ vpc_pcx.peer_vpc.cidr_block }}</cidrBlock> <cidrBlock>{{ vpc_pcx.peer_vpc.cidr_block }}</cidrBlock>
<peeringOptions> <peeringOptions>
@ -101,6 +109,7 @@ DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
</vpcPeeringConnectionSet> </vpcPeeringConnectionSet>
</DescribeVpcPeeringConnectionsResponse> </DescribeVpcPeeringConnectionsResponse>
""" """
)
DELETE_VPC_PEERING_CONNECTION_RESPONSE = """ DELETE_VPC_PEERING_CONNECTION_RESPONSE = """
<DeleteVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> <DeleteVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
@ -109,7 +118,8 @@ DELETE_VPC_PEERING_CONNECTION_RESPONSE = """
</DeleteVpcPeeringConnectionResponse> </DeleteVpcPeeringConnectionResponse>
""" """
ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """ ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = (
"""
<AcceptVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/"> <AcceptVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId> <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<vpcPeeringConnection> <vpcPeeringConnection>
@ -120,7 +130,9 @@ ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock> <cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
</requesterVpcInfo> </requesterVpcInfo>
<accepterVpcInfo> <accepterVpcInfo>
<ownerId>123456789012</ownerId> <ownerId>"""
+ ACCOUNT_ID
+ """</ownerId>
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId> <vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
<cidrBlock>{{ vpc_pcx.peer_vpc.cidr_block }}</cidrBlock> <cidrBlock>{{ vpc_pcx.peer_vpc.cidr_block }}</cidrBlock>
<peeringOptions> <peeringOptions>
@ -137,6 +149,7 @@ ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
</vpcPeeringConnection> </vpcPeeringConnection>
</AcceptVpcPeeringConnectionResponse> </AcceptVpcPeeringConnectionResponse>
""" """
)
REJECT_VPC_PEERING_CONNECTION_RESPONSE = """ REJECT_VPC_PEERING_CONNECTION_RESPONSE = """
<RejectVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> <RejectVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">

View File

@ -2,6 +2,6 @@ from __future__ import unicode_literals
from .responses import EC2Response from .responses import EC2Response
url_bases = ["https?://ec2.(.+).amazonaws.com(|.cn)"] url_bases = ["https?://ec2\.(.+)\.amazonaws\.com(|\.cn)"]
url_paths = {"{0}/": EC2Response.dispatch} url_paths = {"{0}/": EC2Response.dispatch}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
from __future__ import unicode_literals
from .responses import Ec2InstanceConnectResponse
url_bases = ["https?://ec2-instance-connect\.(.+)\.amazonaws\.com"]
url_paths = {"{0}/$": Ec2InstanceConnectResponse.dispatch}

View File

@ -3,9 +3,10 @@ import re
import uuid import uuid
from datetime import datetime from datetime import datetime
from random import random, randint from random import random, randint
import boto3
import pytz import pytz
from boto3 import Session
from moto.core.exceptions import JsonRESTError from moto.core.exceptions import JsonRESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
@ -1302,7 +1303,10 @@ class EC2ContainerServiceBackend(BaseBackend):
raise NotImplementedError() raise NotImplementedError()
available_regions = boto3.session.Session().get_available_regions("ecs") ecs_backends = {}
ecs_backends = { for region in Session().get_available_regions("ecs"):
region: EC2ContainerServiceBackend(region) for region in available_regions ecs_backends[region] = EC2ContainerServiceBackend(region)
} for region in Session().get_available_regions("ecs", partition_name="aws-us-gov"):
ecs_backends[region] = EC2ContainerServiceBackend(region)
for region in Session().get_available_regions("ecs", partition_name="aws-cn"):
ecs_backends[region] = EC2ContainerServiceBackend(region)

View File

@ -2,8 +2,8 @@ from __future__ import unicode_literals
from datetime import datetime from datetime import datetime
from datetime import timedelta from datetime import timedelta
import boto.emr
import pytz import pytz
from boto3 import Session
from dateutil.parser import parse as dtparse from dateutil.parser import parse as dtparse
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.emr.exceptions import EmrError from moto.emr.exceptions import EmrError
@ -460,5 +460,9 @@ class ElasticMapReduceBackend(BaseBackend):
emr_backends = {} emr_backends = {}
for region in boto.emr.regions(): for region in Session().get_available_regions("emr"):
emr_backends[region.name] = ElasticMapReduceBackend(region.name) emr_backends[region] = ElasticMapReduceBackend(region)
for region in Session().get_available_regions("emr", partition_name="aws-us-gov"):
emr_backends[region] = ElasticMapReduceBackend(region)
for region in Session().get_available_regions("emr", partition_name="aws-cn"):
emr_backends[region] = ElasticMapReduceBackend(region)

View File

@ -1,7 +1,7 @@
import os import os
import re import re
import json import json
import boto3 from boto3 import Session
from moto.core.exceptions import JsonRESTError from moto.core.exceptions import JsonRESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -362,5 +362,10 @@ class EventsBackend(BaseBackend):
self.event_buses.pop(name, None) self.event_buses.pop(name, None)
available_regions = boto3.session.Session().get_available_regions("events") events_backends = {}
events_backends = {region: EventsBackend(region) for region in available_regions} for region in Session().get_available_regions("events"):
events_backends[region] = EventsBackend(region)
for region in Session().get_available_regions("events", partition_name="aws-us-gov"):
events_backends[region] = EventsBackend(region)
for region in Session().get_available_regions("events", partition_name="aws-cn"):
events_backends[region] = EventsBackend(region)

View File

@ -4,8 +4,8 @@ import hashlib
import datetime import datetime
from boto3 import Session
import boto.glacier
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from .utils import get_job_id from .utils import get_job_id
@ -221,5 +221,9 @@ class GlacierBackend(BaseBackend):
glacier_backends = {} glacier_backends = {}
for region in boto.glacier.regions(): for region in Session().get_available_regions("glacier"):
glacier_backends[region.name] = GlacierBackend(region) glacier_backends[region] = GlacierBackend(region)
for region in Session().get_available_regions("glacier", partition_name="aws-us-gov"):
glacier_backends[region] = GlacierBackend(region)
for region in Session().get_available_regions("glacier", partition_name="aws-cn"):
glacier_backends[region] = GlacierBackend(region)

View File

@ -14,7 +14,7 @@ from cryptography.hazmat.backends import default_backend
from six.moves.urllib.parse import urlparse from six.moves.urllib.parse import urlparse
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel, ACCOUNT_ID
from moto.core.utils import ( from moto.core.utils import (
iso_8601_datetime_without_milliseconds, iso_8601_datetime_without_milliseconds,
iso_8601_datetime_with_milliseconds, iso_8601_datetime_with_milliseconds,
@ -45,8 +45,6 @@ from .utils import (
random_policy_id, random_policy_id,
) )
ACCOUNT_ID = 123456789012
class MFADevice(object): class MFADevice(object):
"""MFA Device class.""" """MFA Device class."""
@ -309,6 +307,7 @@ class Role(BaseModel):
permissions_boundary, permissions_boundary,
description, description,
tags, tags,
max_session_duration,
): ):
self.id = role_id self.id = role_id
self.name = name self.name = name
@ -320,6 +319,7 @@ class Role(BaseModel):
self.tags = tags self.tags = tags
self.description = description self.description = description
self.permissions_boundary = permissions_boundary self.permissions_boundary = permissions_boundary
self.max_session_duration = max_session_duration
@property @property
def created_iso_8601(self): def created_iso_8601(self):
@ -338,6 +338,7 @@ class Role(BaseModel):
permissions_boundary=properties.get("PermissionsBoundary", ""), permissions_boundary=properties.get("PermissionsBoundary", ""),
description=properties.get("Description", ""), description=properties.get("Description", ""),
tags=properties.get("Tags", {}), tags=properties.get("Tags", {}),
max_session_duration=properties.get("MaxSessionDuration", 3600),
) )
policies = properties.get("Policies", []) policies = properties.get("Policies", [])
@ -542,7 +543,7 @@ class Group(BaseModel):
class User(BaseModel): class User(BaseModel):
def __init__(self, name, path=None): def __init__(self, name, path=None, tags=None):
self.name = name self.name = name
self.id = random_resource_id() self.id = random_resource_id()
self.path = path if path else "/" self.path = path if path else "/"
@ -555,6 +556,7 @@ class User(BaseModel):
self.password = None self.password = None
self.password_reset_required = False self.password_reset_required = False
self.signing_certificates = {} self.signing_certificates = {}
self.tags = tags
@property @property
def arn(self): def arn(self):
@ -938,9 +940,10 @@ class IAMBackend(BaseBackend):
role.description = role_description role.description = role_description
return role return role
def update_role(self, role_name, role_description): def update_role(self, role_name, role_description, max_session_duration):
role = self.get_role(role_name) role = self.get_role(role_name)
role.description = role_description role.description = role_description
role.max_session_duration = max_session_duration
return role return role
def detach_role_policy(self, policy_arn, role_name): def detach_role_policy(self, policy_arn, role_name):
@ -1059,6 +1062,7 @@ class IAMBackend(BaseBackend):
permissions_boundary, permissions_boundary,
description, description,
tags, tags,
max_session_duration,
): ):
role_id = random_resource_id() role_id = random_resource_id()
if permissions_boundary and not self.policy_arn_regex.match( if permissions_boundary and not self.policy_arn_regex.match(
@ -1084,6 +1088,7 @@ class IAMBackend(BaseBackend):
permissions_boundary, permissions_boundary,
description, description,
clean_tags, clean_tags,
max_session_duration,
) )
self.roles[role_id] = role self.roles[role_id] = role
return role return role
@ -1417,13 +1422,13 @@ class IAMBackend(BaseBackend):
"The group with name {0} cannot be found.".format(group_name) "The group with name {0} cannot be found.".format(group_name)
) )
def create_user(self, user_name, path="/"): def create_user(self, user_name, path="/", tags=None):
if user_name in self.users: if user_name in self.users:
raise IAMConflictException( raise IAMConflictException(
"EntityAlreadyExists", "User {0} already exists".format(user_name) "EntityAlreadyExists", "User {0} already exists".format(user_name)
) )
user = User(user_name, path) user = User(user_name, path, tags)
self.users[user_name] = user self.users[user_name] = user
return user return user
@ -1579,6 +1584,10 @@ class IAMBackend(BaseBackend):
user = self.get_user(user_name) user = self.get_user(user_name)
return user.policies.keys() return user.policies.keys()
def list_user_tags(self, user_name):
user = self.get_user(user_name)
return user.tags
def put_user_policy(self, user_name, policy_name, policy_json): def put_user_policy(self, user_name, policy_name, policy_json):
user = self.get_user(user_name) user = self.get_user(user_name)

View File

@ -182,6 +182,7 @@ class IamResponse(BaseResponse):
permissions_boundary = self._get_param("PermissionsBoundary") permissions_boundary = self._get_param("PermissionsBoundary")
description = self._get_param("Description") description = self._get_param("Description")
tags = self._get_multi_param("Tags.member") tags = self._get_multi_param("Tags.member")
max_session_duration = self._get_param("MaxSessionDuration", 3600)
role = iam_backend.create_role( role = iam_backend.create_role(
role_name, role_name,
@ -190,6 +191,7 @@ class IamResponse(BaseResponse):
permissions_boundary, permissions_boundary,
description, description,
tags, tags,
max_session_duration,
) )
template = self.response_template(CREATE_ROLE_TEMPLATE) template = self.response_template(CREATE_ROLE_TEMPLATE)
return template.render(role=role) return template.render(role=role)
@ -258,7 +260,8 @@ class IamResponse(BaseResponse):
def update_role(self): def update_role(self):
role_name = self._get_param("RoleName") role_name = self._get_param("RoleName")
description = self._get_param("Description") description = self._get_param("Description")
role = iam_backend.update_role(role_name, description) max_session_duration = self._get_param("MaxSessionDuration", 3600)
role = iam_backend.update_role(role_name, description, max_session_duration)
template = self.response_template(UPDATE_ROLE_TEMPLATE) template = self.response_template(UPDATE_ROLE_TEMPLATE)
return template.render(role=role) return template.render(role=role)
@ -437,8 +440,8 @@ class IamResponse(BaseResponse):
def create_user(self): def create_user(self):
user_name = self._get_param("UserName") user_name = self._get_param("UserName")
path = self._get_param("Path") path = self._get_param("Path")
tags = self._get_multi_param("Tags.member")
user = iam_backend.create_user(user_name, path) user = iam_backend.create_user(user_name, path, tags)
template = self.response_template(USER_TEMPLATE) template = self.response_template(USER_TEMPLATE)
return template.render(action="Create", user=user) return template.render(action="Create", user=user)
@ -535,6 +538,12 @@ class IamResponse(BaseResponse):
template = self.response_template(LIST_USER_POLICIES_TEMPLATE) template = self.response_template(LIST_USER_POLICIES_TEMPLATE)
return template.render(policies=policies) return template.render(policies=policies)
def list_user_tags(self):
user_name = self._get_param("UserName")
tags = iam_backend.list_user_tags(user_name)
template = self.response_template(LIST_USER_TAGS_TEMPLATE)
return template.render(user_tags=tags or [])
def put_user_policy(self): def put_user_policy(self):
user_name = self._get_param("UserName") user_name = self._get_param("UserName")
policy_name = self._get_param("PolicyName") policy_name = self._get_param("PolicyName")
@ -1189,9 +1198,12 @@ CREATE_ROLE_TEMPLATE = """<CreateRoleResponse xmlns="https://iam.amazonaws.com/d
<Arn>{{ role.arn }}</Arn> <Arn>{{ role.arn }}</Arn>
<RoleName>{{ role.name }}</RoleName> <RoleName>{{ role.name }}</RoleName>
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
{% if role.description %}
<Description>{{role.description}}</Description> <Description>{{role.description}}</Description>
{% endif %}
<CreateDate>{{ role.created_iso_8601 }}</CreateDate> <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
<RoleId>{{ role.id }}</RoleId> <RoleId>{{ role.id }}</RoleId>
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
{% if role.permissions_boundary %} {% if role.permissions_boundary %}
<PermissionsBoundary> <PermissionsBoundary>
<PermissionsBoundaryType>PermissionsBoundaryPolicy</PermissionsBoundaryType> <PermissionsBoundaryType>PermissionsBoundaryPolicy</PermissionsBoundaryType>
@ -1244,6 +1256,7 @@ UPDATE_ROLE_DESCRIPTION_TEMPLATE = """<UpdateRoleDescriptionResponse xmlns="http
<Description>{{role.description}}</Description> <Description>{{role.description}}</Description>
<CreateDate>{{ role.created_iso_8601 }}</CreateDate> <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
<RoleId>{{ role.id }}</RoleId> <RoleId>{{ role.id }}</RoleId>
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
{% if role.tags %} {% if role.tags %}
<Tags> <Tags>
{% for tag in role.get_tags() %} {% for tag in role.get_tags() %}
@ -1268,9 +1281,12 @@ GET_ROLE_TEMPLATE = """<GetRoleResponse xmlns="https://iam.amazonaws.com/doc/201
<Arn>{{ role.arn }}</Arn> <Arn>{{ role.arn }}</Arn>
<RoleName>{{ role.name }}</RoleName> <RoleName>{{ role.name }}</RoleName>
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
{% if role.description %}
<Description>{{role.description}}</Description> <Description>{{role.description}}</Description>
{% endif %}
<CreateDate>{{ role.created_iso_8601 }}</CreateDate> <CreateDate>{{ role.created_iso_8601 }}</CreateDate>
<RoleId>{{ role.id }}</RoleId> <RoleId>{{ role.id }}</RoleId>
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
{% if role.tags %} {% if role.tags %}
<Tags> <Tags>
{% for tag in role.get_tags() %} {% for tag in role.get_tags() %}
@ -1689,6 +1705,23 @@ LIST_USER_POLICIES_TEMPLATE = """<ListUserPoliciesResponse>
</ResponseMetadata> </ResponseMetadata>
</ListUserPoliciesResponse>""" </ListUserPoliciesResponse>"""
LIST_USER_TAGS_TEMPLATE = """<ListUserTagsResponse>
<ListUserTagsResult>
<Tags>
{% for tag in user_tags %}
<item>
<Key>{{ tag.Key }}</Key>
<Value>{{ tag.Value }}</Value>
</item>
{% endfor %}
</Tags>
<IsTruncated>false</IsTruncated>
</ListUserTagsResult>
<ResponseMetadata>
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ResponseMetadata>
</ListUserTagsResponse>"""
CREATE_ACCESS_KEY_TEMPLATE = """<CreateAccessKeyResponse> CREATE_ACCESS_KEY_TEMPLATE = """<CreateAccessKeyResponse>
<CreateAccessKeyResult> <CreateAccessKeyResult>
<AccessKey> <AccessKey>

View File

@ -9,7 +9,7 @@ import uuid
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime from datetime import datetime
import boto3 from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from .exceptions import ( from .exceptions import (
@ -825,5 +825,10 @@ class IoTBackend(BaseBackend):
return self.jobs[job_id] return self.jobs[job_id]
available_regions = boto3.session.Session().get_available_regions("iot") iot_backends = {}
iot_backends = {region: IoTBackend(region) for region in available_regions} for region in Session().get_available_regions("iot"):
iot_backends[region] = IoTBackend(region)
for region in Session().get_available_regions("iot", partition_name="aws-us-gov"):
iot_backends[region] = IoTBackend(region)
for region in Session().get_available_regions("iot", partition_name="aws-cn"):
iot_backends[region] = IoTBackend(region)

View File

@ -1,8 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import json import json
import time import time
import boto3
import jsondiff import jsondiff
from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.iot import iot_backends from moto.iot import iot_backends
from .exceptions import ( from .exceptions import (
@ -205,5 +206,10 @@ class IoTDataPlaneBackend(BaseBackend):
return None return None
available_regions = boto3.session.Session().get_available_regions("iot-data") iotdata_backends = {}
iotdata_backends = {region: IoTDataPlaneBackend(region) for region in available_regions} for region in Session().get_available_regions("iot-data"):
iotdata_backends[region] = IoTDataPlaneBackend(region)
for region in Session().get_available_regions("iot-data", partition_name="aws-us-gov"):
iotdata_backends[region] = IoTDataPlaneBackend(region)
for region in Session().get_available_regions("iot-data", partition_name="aws-cn"):
iotdata_backends[region] = IoTDataPlaneBackend(region)

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
import json import json
from werkzeug.exceptions import BadRequest from werkzeug.exceptions import BadRequest
from moto.core import ACCOUNT_ID
class ResourceNotFoundError(BadRequest): class ResourceNotFoundError(BadRequest):
@ -23,14 +24,14 @@ class ResourceInUseError(BadRequest):
class StreamNotFoundError(ResourceNotFoundError): class StreamNotFoundError(ResourceNotFoundError):
def __init__(self, stream_name): def __init__(self, stream_name):
super(StreamNotFoundError, self).__init__( super(StreamNotFoundError, self).__init__(
"Stream {0} under account 123456789012 not found.".format(stream_name) "Stream {0} under account {1} not found.".format(stream_name, ACCOUNT_ID)
) )
class ShardNotFoundError(ResourceNotFoundError): class ShardNotFoundError(ResourceNotFoundError):
def __init__(self, shard_id): def __init__(self, shard_id):
super(ShardNotFoundError, self).__init__( super(ShardNotFoundError, self).__init__(
"Shard {0} under account 123456789012 not found.".format(shard_id) "Shard {0} under account {1} not found.".format(shard_id, ACCOUNT_ID)
) )

View File

@ -2,7 +2,6 @@ from __future__ import unicode_literals
import datetime import datetime
import time import time
import boto.kinesis
import re import re
import six import six
import itertools import itertools
@ -10,9 +9,12 @@ import itertools
from operator import attrgetter from operator import attrgetter
from hashlib import md5 from hashlib import md5
from boto3 import Session
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
from moto.core import ACCOUNT_ID
from .exceptions import ( from .exceptions import (
StreamNotFoundError, StreamNotFoundError,
ShardNotFoundError, ShardNotFoundError,
@ -133,7 +135,7 @@ class Stream(BaseModel):
self.shard_count = shard_count self.shard_count = shard_count
self.creation_datetime = datetime.datetime.now() self.creation_datetime = datetime.datetime.now()
self.region = region self.region = region
self.account_number = "123456789012" self.account_number = ACCOUNT_ID
self.shards = {} self.shards = {}
self.tags = {} self.tags = {}
self.status = "ACTIVE" self.status = "ACTIVE"
@ -259,8 +261,8 @@ class DeliveryStream(BaseModel):
@property @property
def arn(self): def arn(self):
return "arn:aws:firehose:us-east-1:123456789012:deliverystream/{0}".format( return "arn:aws:firehose:us-east-1:{1}:deliverystream/{0}".format(
self.name self.name, ACCOUNT_ID
) )
def destinations_to_dict(self): def destinations_to_dict(self):
@ -529,5 +531,9 @@ class KinesisBackend(BaseBackend):
kinesis_backends = {} kinesis_backends = {}
for region in boto.kinesis.regions(): for region in Session().get_available_regions("kinesis"):
kinesis_backends[region.name] = KinesisBackend() kinesis_backends[region] = KinesisBackend()
for region in Session().get_available_regions("kinesis", partition_name="aws-us-gov"):
kinesis_backends[region] = KinesisBackend()
for region in Session().get_available_regions("kinesis", partition_name="aws-cn"):
kinesis_backends[region] = KinesisBackend()

View File

@ -4,7 +4,7 @@ import os
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
import boto.kms from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_without_milliseconds from moto.core.utils import iso_8601_datetime_without_milliseconds
@ -284,5 +284,9 @@ class KmsBackend(BaseBackend):
kms_backends = {} kms_backends = {}
for region in boto.kms.regions(): for region in Session().get_available_regions("kms"):
kms_backends[region.name] = KmsBackend() kms_backends[region] = KmsBackend()
for region in Session().get_available_regions("kms", partition_name="aws-us-gov"):
kms_backends[region] = KmsBackend()
for region in Session().get_available_regions("kms", partition_name="aws-cn"):
kms_backends[region] = KmsBackend()

View File

@ -1,5 +1,6 @@
from boto3 import Session
from moto.core import BaseBackend from moto.core import BaseBackend
import boto.logs
from moto.core.utils import unix_time_millis from moto.core.utils import unix_time_millis
from .exceptions import ( from .exceptions import (
ResourceNotFoundException, ResourceNotFoundException,
@ -558,6 +559,10 @@ class LogsBackend(BaseBackend):
log_group.untag(tags) log_group.untag(tags)
logs_backends = { logs_backends = {}
region.name: LogsBackend(region.name) for region in boto.logs.regions() for region in Session().get_available_regions("logs"):
} logs_backends[region] = LogsBackend(region)
for region in Session().get_available_regions("logs", partition_name="aws-us-gov"):
logs_backends[region] = LogsBackend(region)
for region in Session().get_available_regions("logs", partition_name="aws-cn"):
logs_backends[region] = LogsBackend(region)

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
from moto.core import ACCOUNT_ID
import uuid import uuid
import datetime import datetime
from random import choice from random import choice
@ -367,7 +368,7 @@ class Stack(BaseModel):
self.id = "{0}".format(uuid.uuid4()) self.id = "{0}".format(uuid.uuid4())
self.layers = [] self.layers = []
self.apps = [] self.apps = []
self.account_number = "123456789012" self.account_number = ACCOUNT_ID
self.created_at = datetime.datetime.utcnow() self.created_at = datetime.datetime.utcnow()
def __eq__(self, other): def __eq__(self, other):

View File

@ -2,8 +2,10 @@ from __future__ import unicode_literals
import random import random
import string import string
from moto.core import ACCOUNT_ID
MASTER_ACCOUNT_ID = "123456789012"
MASTER_ACCOUNT_ID = ACCOUNT_ID
MASTER_ACCOUNT_EMAIL = "master@example.com" MASTER_ACCOUNT_EMAIL = "master@example.com"
DEFAULT_POLICY_ID = "p-FullAWSAccess" DEFAULT_POLICY_ID = "p-FullAWSAccess"
ORGANIZATION_ARN_FORMAT = "arn:aws:organizations::{0}:organization/{1}" ORGANIZATION_ARN_FORMAT = "arn:aws:organizations::{0}:organization/{1}"

View File

@ -2,13 +2,14 @@ from __future__ import unicode_literals
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
import datetime import datetime
import boto3 from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from .resources import VOICE_DATA from .resources import VOICE_DATA
from .utils import make_arn_for_lexicon from .utils import make_arn_for_lexicon
DEFAULT_ACCOUNT_ID = 123456789012 from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
class Lexicon(BaseModel): class Lexicon(BaseModel):
@ -113,7 +114,10 @@ class PollyBackend(BaseBackend):
self._lexicons[name] = lexicon self._lexicons[name] = lexicon
available_regions = boto3.session.Session().get_available_regions("polly") polly_backends = {}
polly_backends = { for region in Session().get_available_regions("polly"):
region: PollyBackend(region_name=region) for region in available_regions polly_backends[region] = PollyBackend(region)
} for region in Session().get_available_regions("polly", partition_name="aws-us-gov"):
polly_backends[region] = PollyBackend(region)
for region in Session().get_available_regions("polly", partition_name="aws-cn"):
polly_backends[region] = PollyBackend(region)

View File

@ -5,7 +5,7 @@ import datetime
import os import os
from collections import defaultdict from collections import defaultdict
import boto.rds2 from boto3 import Session
from jinja2 import Template from jinja2 import Template
from re import compile as re_compile from re import compile as re_compile
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
@ -1501,6 +1501,10 @@ class DBParameterGroup(object):
return db_parameter_group return db_parameter_group
rds2_backends = dict( rds2_backends = {}
(region.name, RDS2Backend(region.name)) for region in boto.rds2.regions() for region in Session().get_available_regions("rds"):
) rds2_backends[region] = RDS2Backend(region)
for region in Session().get_available_regions("rds", partition_name="aws-us-gov"):
rds2_backends[region] = RDS2Backend(region)
for region in Session().get_available_regions("rds", partition_name="aws-cn"):
rds2_backends[region] = RDS2Backend(region)

View File

@ -3,7 +3,7 @@ from __future__ import unicode_literals
import copy import copy
import datetime import datetime
import boto.redshift from boto3 import Session
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -27,7 +27,7 @@ from .exceptions import (
) )
ACCOUNT_ID = 123456789012 from moto.core import ACCOUNT_ID
class TaggableResourceMixin(object): class TaggableResourceMixin(object):
@ -897,7 +897,9 @@ class RedshiftBackend(BaseBackend):
redshift_backends = {} redshift_backends = {}
for region in boto.redshift.regions(): for region in Session().get_available_regions("redshift"):
redshift_backends[region.name] = RedshiftBackend( redshift_backends[region] = RedshiftBackend(ec2_backends[region], region)
ec2_backends[region.name], region.name for region in Session().get_available_regions("redshift", partition_name="aws-us-gov"):
) redshift_backends[region] = RedshiftBackend(ec2_backends[region], region)
for region in Session().get_available_regions("redshift", partition_name="aws-cn"):
redshift_backends[region] = RedshiftBackend(ec2_backends[region], region)

View File

@ -1,11 +1,13 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from builtins import str from builtins import str
import boto3
import json import json
import re import re
from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core import ACCOUNT_ID
from .exceptions import BadRequestException from .exceptions import BadRequestException
@ -23,8 +25,8 @@ class FakeResourceGroup(BaseModel):
if self._validate_tags(value=tags): if self._validate_tags(value=tags):
self._tags = tags self._tags = tags
self._raise_errors() self._raise_errors()
self.arn = "arn:aws:resource-groups:us-west-1:123456789012:{name}".format( self.arn = "arn:aws:resource-groups:us-west-1:{AccountId}:{name}".format(
name=name name=name, AccountId=ACCOUNT_ID
) )
@staticmethod @staticmethod
@ -349,7 +351,14 @@ class ResourceGroupsBackend(BaseBackend):
return self.groups.by_name[group_name] return self.groups.by_name[group_name]
available_regions = boto3.session.Session().get_available_regions("resource-groups") resourcegroups_backends = {}
resourcegroups_backends = { for region in Session().get_available_regions("resource-groups"):
region: ResourceGroupsBackend(region_name=region) for region in available_regions resourcegroups_backends[region] = ResourceGroupsBackend(region)
} for region in Session().get_available_regions(
"resource-groups", partition_name="aws-us-gov"
):
resourcegroups_backends[region] = ResourceGroupsBackend(region)
for region in Session().get_available_regions(
"resource-groups", partition_name="aws-cn"
):
resourcegroups_backends[region] = ResourceGroupsBackend(region)

View File

@ -1,7 +1,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import uuid import uuid
import boto3
import six import six
from boto3 import Session
from moto.core import BaseBackend from moto.core import BaseBackend
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
@ -636,9 +637,14 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
# return failed_resources_map # return failed_resources_map
available_regions = boto3.session.Session().get_available_regions( resourcegroupstaggingapi_backends = {}
"resourcegroupstaggingapi" for region in Session().get_available_regions("resourcegroupstaggingapi"):
) resourcegroupstaggingapi_backends[region] = ResourceGroupsTaggingAPIBackend(region)
resourcegroupstaggingapi_backends = { for region in Session().get_available_regions(
region: ResourceGroupsTaggingAPIBackend(region) for region in available_regions "resourcegroupstaggingapi", partition_name="aws-us-gov"
} ):
resourcegroupstaggingapi_backends[region] = ResourceGroupsTaggingAPIBackend(region)
for region in Session().get_available_regions(
"resourcegroupstaggingapi", partition_name="aws-cn"
):
resourcegroupstaggingapi_backends[region] = ResourceGroupsTaggingAPIBackend(region)

View File

@ -323,3 +323,27 @@ class BucketSignatureDoesNotMatchError(S3ClientError):
*args, *args,
**kwargs **kwargs
) )
class NoSuchPublicAccessBlockConfiguration(S3ClientError):
code = 404
def __init__(self, *args, **kwargs):
super(NoSuchPublicAccessBlockConfiguration, self).__init__(
"NoSuchPublicAccessBlockConfiguration",
"The public access block configuration was not found",
*args,
**kwargs
)
class InvalidPublicAccessBlockConfiguration(S3ClientError):
code = 400
def __init__(self, *args, **kwargs):
super(InvalidPublicAccessBlockConfiguration, self).__init__(
"InvalidRequest",
"Must specify at least one configuration.",
*args,
**kwargs
)

View File

@ -35,6 +35,8 @@ from .exceptions import (
InvalidTargetBucketForLogging, InvalidTargetBucketForLogging,
DuplicateTagKeys, DuplicateTagKeys,
CrossLocationLoggingProhibitted, CrossLocationLoggingProhibitted,
NoSuchPublicAccessBlockConfiguration,
InvalidPublicAccessBlockConfiguration,
) )
from .utils import clean_key_name, _VersionedKeyStore from .utils import clean_key_name, _VersionedKeyStore
@ -659,11 +661,8 @@ class Notification(BaseModel):
else: else:
data["filter"] = None data["filter"] = None
data[ # Not sure why this is a thing since AWS just seems to return this as filters ¯\_(ツ)_/¯
"objectPrefixes" data["objectPrefixes"] = []
] = (
[]
) # Not sure why this is a thing since AWS just seems to return this as filters ¯\_(ツ)_/¯
return data return data
@ -728,6 +727,38 @@ class NotificationConfiguration(BaseModel):
return data return data
def convert_str_to_bool(item):
"""Converts a boolean string to a boolean value"""
if isinstance(item, str):
return item.lower() == "true"
return False
class PublicAccessBlock(BaseModel):
def __init__(
self,
block_public_acls,
ignore_public_acls,
block_public_policy,
restrict_public_buckets,
):
# The boto XML appears to expect these values to exist as lowercase strings...
self.block_public_acls = block_public_acls or "false"
self.ignore_public_acls = ignore_public_acls or "false"
self.block_public_policy = block_public_policy or "false"
self.restrict_public_buckets = restrict_public_buckets or "false"
def to_config_dict(self):
# Need to make the string values booleans for Config:
return {
"blockPublicAcls": convert_str_to_bool(self.block_public_acls),
"ignorePublicAcls": convert_str_to_bool(self.ignore_public_acls),
"blockPublicPolicy": convert_str_to_bool(self.block_public_policy),
"restrictPublicBuckets": convert_str_to_bool(self.restrict_public_buckets),
}
class FakeBucket(BaseModel): class FakeBucket(BaseModel):
def __init__(self, name, region_name): def __init__(self, name, region_name):
self.name = name self.name = name
@ -746,6 +777,7 @@ class FakeBucket(BaseModel):
self.accelerate_configuration = None self.accelerate_configuration = None
self.payer = "BucketOwner" self.payer = "BucketOwner"
self.creation_date = datetime.datetime.utcnow() self.creation_date = datetime.datetime.utcnow()
self.public_access_block = None
@property @property
def location(self): def location(self):
@ -1079,13 +1111,16 @@ class FakeBucket(BaseModel):
} }
# Make the supplementary configuration: # Make the supplementary configuration:
# TODO: Implement Public Access Block Support
# This is a dobule-wrapped JSON for some reason... # This is a dobule-wrapped JSON for some reason...
s_config = { s_config = {
"AccessControlList": json.dumps(json.dumps(self.acl.to_config_dict())) "AccessControlList": json.dumps(json.dumps(self.acl.to_config_dict()))
} }
if self.public_access_block:
s_config["PublicAccessBlockConfiguration"] = json.dumps(
self.public_access_block.to_config_dict()
)
# Tagging is special: # Tagging is special:
if config_dict["tags"]: if config_dict["tags"]:
s_config["BucketTaggingConfiguration"] = json.dumps( s_config["BucketTaggingConfiguration"] = json.dumps(
@ -1221,6 +1256,14 @@ class S3Backend(BaseBackend):
bucket = self.get_bucket(bucket_name) bucket = self.get_bucket(bucket_name)
return bucket.website_configuration return bucket.website_configuration
def get_bucket_public_access_block(self, bucket_name):
bucket = self.get_bucket(bucket_name)
if not bucket.public_access_block:
raise NoSuchPublicAccessBlockConfiguration()
return bucket.public_access_block
def set_key( def set_key(
self, bucket_name, key_name, value, storage=None, etag=None, multipart=None self, bucket_name, key_name, value, storage=None, etag=None, multipart=None
): ):
@ -1309,6 +1352,10 @@ class S3Backend(BaseBackend):
bucket = self.get_bucket(bucket_name) bucket = self.get_bucket(bucket_name)
bucket.delete_cors() bucket.delete_cors()
def delete_bucket_public_access_block(self, bucket_name):
bucket = self.get_bucket(bucket_name)
bucket.public_access_block = None
def put_bucket_notification_configuration(self, bucket_name, notification_config): def put_bucket_notification_configuration(self, bucket_name, notification_config):
bucket = self.get_bucket(bucket_name) bucket = self.get_bucket(bucket_name)
bucket.set_notification_configuration(notification_config) bucket.set_notification_configuration(notification_config)
@ -1324,6 +1371,19 @@ class S3Backend(BaseBackend):
raise InvalidRequest("PutBucketAccelerateConfiguration") raise InvalidRequest("PutBucketAccelerateConfiguration")
bucket.set_accelerate_configuration(accelerate_configuration) bucket.set_accelerate_configuration(accelerate_configuration)
def put_bucket_public_access_block(self, bucket_name, pub_block_config):
bucket = self.get_bucket(bucket_name)
if not pub_block_config:
raise InvalidPublicAccessBlockConfiguration()
bucket.public_access_block = PublicAccessBlock(
pub_block_config.get("BlockPublicAcls"),
pub_block_config.get("IgnorePublicAcls"),
pub_block_config.get("BlockPublicPolicy"),
pub_block_config.get("RestrictPublicBuckets"),
)
def initiate_multipart(self, bucket_name, key_name, metadata): def initiate_multipart(self, bucket_name, key_name, metadata):
bucket = self.get_bucket(bucket_name) bucket = self.get_bucket(bucket_name)
new_multipart = FakeMultipart(key_name, metadata) new_multipart = FakeMultipart(key_name, metadata)

View File

@ -1,10 +1,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
import sys
import six import six
from moto.core.utils import str_to_rfc_1123_datetime from moto.core.utils import str_to_rfc_1123_datetime, py2_strip_unicode_keys
from six.moves.urllib.parse import parse_qs, urlparse, unquote from six.moves.urllib.parse import parse_qs, urlparse, unquote
import xmltodict import xmltodict
@ -12,6 +13,7 @@ import xmltodict
from moto.packages.httpretty.core import HTTPrettyRequest from moto.packages.httpretty.core import HTTPrettyRequest
from moto.core.responses import _TemplateEnvironmentMixin, ActionAuthenticatorMixin from moto.core.responses import _TemplateEnvironmentMixin, ActionAuthenticatorMixin
from moto.core.utils import path_url from moto.core.utils import path_url
from moto.core import ACCOUNT_ID
from moto.s3bucket_path.utils import ( from moto.s3bucket_path.utils import (
bucket_name_from_url as bucketpath_bucket_name_from_url, bucket_name_from_url as bucketpath_bucket_name_from_url,
@ -70,6 +72,7 @@ ACTION_MAP = {
"notification": "GetBucketNotification", "notification": "GetBucketNotification",
"accelerate": "GetAccelerateConfiguration", "accelerate": "GetAccelerateConfiguration",
"versions": "ListBucketVersions", "versions": "ListBucketVersions",
"public_access_block": "GetPublicAccessBlock",
"DEFAULT": "ListBucket", "DEFAULT": "ListBucket",
}, },
"PUT": { "PUT": {
@ -83,6 +86,7 @@ ACTION_MAP = {
"cors": "PutBucketCORS", "cors": "PutBucketCORS",
"notification": "PutBucketNotification", "notification": "PutBucketNotification",
"accelerate": "PutAccelerateConfiguration", "accelerate": "PutAccelerateConfiguration",
"public_access_block": "PutPublicAccessBlock",
"DEFAULT": "CreateBucket", "DEFAULT": "CreateBucket",
}, },
"DELETE": { "DELETE": {
@ -90,6 +94,7 @@ ACTION_MAP = {
"policy": "DeleteBucketPolicy", "policy": "DeleteBucketPolicy",
"tagging": "PutBucketTagging", "tagging": "PutBucketTagging",
"cors": "PutBucketCORS", "cors": "PutBucketCORS",
"public_access_block": "DeletePublicAccessBlock",
"DEFAULT": "DeleteBucket", "DEFAULT": "DeleteBucket",
}, },
}, },
@ -399,6 +404,12 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
return 200, {}, template.render() return 200, {}, template.render()
template = self.response_template(S3_BUCKET_ACCELERATE) template = self.response_template(S3_BUCKET_ACCELERATE)
return template.render(bucket=bucket) return template.render(bucket=bucket)
elif "publicAccessBlock" in querystring:
public_block_config = self.backend.get_bucket_public_access_block(
bucket_name
)
template = self.response_template(S3_PUBLIC_ACCESS_BLOCK_CONFIGURATION)
return template.render(public_block_config=public_block_config)
elif "versions" in querystring: elif "versions" in querystring:
delimiter = querystring.get("delimiter", [None])[0] delimiter = querystring.get("delimiter", [None])[0]
@ -651,6 +662,23 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
except Exception as e: except Exception as e:
raise e raise e
elif "publicAccessBlock" in querystring:
parsed_xml = xmltodict.parse(body)
parsed_xml["PublicAccessBlockConfiguration"].pop("@xmlns", None)
# If Python 2, fix the unicode strings:
if sys.version_info[0] < 3:
parsed_xml = {
"PublicAccessBlockConfiguration": py2_strip_unicode_keys(
dict(parsed_xml["PublicAccessBlockConfiguration"])
)
}
self.backend.put_bucket_public_access_block(
bucket_name, parsed_xml["PublicAccessBlockConfiguration"]
)
return ""
else: else:
if body: if body:
# us-east-1, the default AWS region behaves a bit differently # us-east-1, the default AWS region behaves a bit differently
@ -706,6 +734,9 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
bucket = self.backend.get_bucket(bucket_name) bucket = self.backend.get_bucket(bucket_name)
bucket.delete_lifecycle() bucket.delete_lifecycle()
return 204, {}, "" return 204, {}, ""
elif "publicAccessBlock" in querystring:
self.backend.delete_bucket_public_access_block(bucket_name)
return 204, {}, ""
removed_bucket = self.backend.delete_bucket(bucket_name) removed_bucket = self.backend.delete_bucket(bucket_name)
@ -1460,7 +1491,9 @@ S3_ALL_BUCKETS = """<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2
S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?> S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>{{ bucket.name }}</Name> <Name>{{ bucket.name }}</Name>
{% if prefix != None %}
<Prefix>{{ prefix }}</Prefix> <Prefix>{{ prefix }}</Prefix>
{% endif %}
<MaxKeys>{{ max_keys }}</MaxKeys> <MaxKeys>{{ max_keys }}</MaxKeys>
<Delimiter>{{ delimiter }}</Delimiter> <Delimiter>{{ delimiter }}</Delimiter>
<IsTruncated>{{ is_truncated }}</IsTruncated> <IsTruncated>{{ is_truncated }}</IsTruncated>
@ -1492,7 +1525,9 @@ S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
S3_BUCKET_GET_RESPONSE_V2 = """<?xml version="1.0" encoding="UTF-8"?> S3_BUCKET_GET_RESPONSE_V2 = """<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>{{ bucket.name }}</Name> <Name>{{ bucket.name }}</Name>
{% if prefix != None %}
<Prefix>{{ prefix }}</Prefix> <Prefix>{{ prefix }}</Prefix>
{% endif %}
<MaxKeys>{{ max_keys }}</MaxKeys> <MaxKeys>{{ max_keys }}</MaxKeys>
<KeyCount>{{ key_count }}</KeyCount> <KeyCount>{{ key_count }}</KeyCount>
{% if delimiter %} {% if delimiter %}
@ -1653,7 +1688,9 @@ S3_BUCKET_GET_VERSIONING = """<?xml version="1.0" encoding="UTF-8"?>
S3_BUCKET_GET_VERSIONS = """<?xml version="1.0" encoding="UTF-8"?> S3_BUCKET_GET_VERSIONS = """<?xml version="1.0" encoding="UTF-8"?>
<ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01"> <ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
<Name>{{ bucket.name }}</Name> <Name>{{ bucket.name }}</Name>
{% if prefix != None %}
<Prefix>{{ prefix }}</Prefix> <Prefix>{{ prefix }}</Prefix>
{% endif %}
<KeyMarker>{{ key_marker }}</KeyMarker> <KeyMarker>{{ key_marker }}</KeyMarker>
<MaxKeys>{{ max_keys }}</MaxKeys> <MaxKeys>{{ max_keys }}</MaxKeys>
<IsTruncated>{{ is_truncated }}</IsTruncated> <IsTruncated>{{ is_truncated }}</IsTruncated>
@ -1856,7 +1893,8 @@ S3_MULTIPART_COMPLETE_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
</CompleteMultipartUploadResult> </CompleteMultipartUploadResult>
""" """
S3_ALL_MULTIPARTS = """<?xml version="1.0" encoding="UTF-8"?> S3_ALL_MULTIPARTS = (
"""<?xml version="1.0" encoding="UTF-8"?>
<ListMultipartUploadsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <ListMultipartUploadsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Bucket>{{ bucket_name }}</Bucket> <Bucket>{{ bucket_name }}</Bucket>
<KeyMarker></KeyMarker> <KeyMarker></KeyMarker>
@ -1868,7 +1906,9 @@ S3_ALL_MULTIPARTS = """<?xml version="1.0" encoding="UTF-8"?>
<Key>{{ upload.key_name }}</Key> <Key>{{ upload.key_name }}</Key>
<UploadId>{{ upload.id }}</UploadId> <UploadId>{{ upload.id }}</UploadId>
<Initiator> <Initiator>
<ID>arn:aws:iam::123456789012:user/user1-11111a31-17b5-4fb7-9df5-b111111f13de</ID> <ID>arn:aws:iam::"""
+ ACCOUNT_ID
+ """:user/user1-11111a31-17b5-4fb7-9df5-b111111f13de</ID>
<DisplayName>user1-11111a31-17b5-4fb7-9df5-b111111f13de</DisplayName> <DisplayName>user1-11111a31-17b5-4fb7-9df5-b111111f13de</DisplayName>
</Initiator> </Initiator>
<Owner> <Owner>
@ -1881,6 +1921,7 @@ S3_ALL_MULTIPARTS = """<?xml version="1.0" encoding="UTF-8"?>
{% endfor %} {% endfor %}
</ListMultipartUploadsResult> </ListMultipartUploadsResult>
""" """
)
S3_NO_POLICY = """<?xml version="1.0" encoding="UTF-8"?> S3_NO_POLICY = """<?xml version="1.0" encoding="UTF-8"?>
<Error> <Error>
@ -2053,3 +2094,12 @@ S3_BUCKET_ACCELERATE = """
S3_BUCKET_ACCELERATE_NOT_SET = """ S3_BUCKET_ACCELERATE_NOT_SET = """
<AccelerateConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"/> <AccelerateConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"/>
""" """
S3_PUBLIC_ACCESS_BLOCK_CONFIGURATION = """
<PublicAccessBlockConfiguration>
<BlockPublicAcls>{{public_block_config.block_public_acls}}</BlockPublicAcls>
<IgnorePublicAcls>{{public_block_config.ignore_public_acls}}</IgnorePublicAcls>
<BlockPublicPolicy>{{public_block_config.block_public_policy}}</BlockPublicPolicy>
<RestrictPublicBuckets>{{public_block_config.restrict_public_buckets}}</RestrictPublicBuckets>
</PublicAccessBlockConfiguration>
"""

View File

@ -6,7 +6,7 @@ import json
import uuid import uuid
import datetime import datetime
import boto3 from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from .exceptions import ( from .exceptions import (
@ -491,7 +491,14 @@ class SecretsManagerBackend(BaseBackend):
) )
available_regions = boto3.session.Session().get_available_regions("secretsmanager") secretsmanager_backends = {}
secretsmanager_backends = { for region in Session().get_available_regions("secretsmanager"):
region: SecretsManagerBackend(region_name=region) for region in available_regions secretsmanager_backends[region] = SecretsManagerBackend(region_name=region)
} for region in Session().get_available_regions(
"secretsmanager", partition_name="aws-us-gov"
):
secretsmanager_backends[region] = SecretsManagerBackend(region_name=region)
for region in Session().get_available_regions(
"secretsmanager", partition_name="aws-cn"
):
secretsmanager_backends[region] = SecretsManagerBackend(region_name=region)

View File

@ -1,3 +1,5 @@
from moto.core import ACCOUNT_ID
""" """
SES Feedback messages SES Feedback messages
Extracted from https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html Extracted from https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html
@ -10,7 +12,7 @@ COMMON_MAIL = {
"source": "sender@example.com", "source": "sender@example.com",
"sourceArn": "arn:aws:ses:us-west-2:888888888888:identity/example.com", "sourceArn": "arn:aws:ses:us-west-2:888888888888:identity/example.com",
"sourceIp": "127.0.3.0", "sourceIp": "127.0.3.0",
"sendingAccountId": "123456789012", "sendingAccountId": ACCOUNT_ID,
"destination": ["recipient@example.com"], "destination": ["recipient@example.com"],
"headersTruncated": False, "headersTruncated": False,
"headers": [ "headers": [

View File

@ -31,7 +31,8 @@ from .exceptions import (
) )
from .utils import make_arn_for_topic, make_arn_for_subscription, is_e164 from .utils import make_arn_for_topic, make_arn_for_subscription, is_e164
DEFAULT_ACCOUNT_ID = 123456789012 from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
DEFAULT_PAGE_SIZE = 100 DEFAULT_PAGE_SIZE = 100
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
@ -259,7 +260,9 @@ class Subscription(BaseModel):
"SignatureVersion": "1", "SignatureVersion": "1",
"Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=", "Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",
"SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem", "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
"UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789012:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55", "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:{}:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55".format(
DEFAULT_ACCOUNT_ID
),
} }
if message_attributes: if message_attributes:
post_data["MessageAttributes"] = message_attributes post_data["MessageAttributes"] = message_attributes
@ -275,8 +278,11 @@ class PlatformApplication(BaseModel):
@property @property
def arn(self): def arn(self):
return "arn:aws:sns:{region}:123456789012:app/{platform}/{name}".format( return "arn:aws:sns:{region}:{AccountId}:app/{platform}/{name}".format(
region=self.region, platform=self.platform, name=self.name region=self.region,
platform=self.platform,
name=self.name,
AccountId=DEFAULT_ACCOUNT_ID,
) )
@ -305,8 +311,9 @@ class PlatformEndpoint(BaseModel):
@property @property
def arn(self): def arn(self):
return "arn:aws:sns:{region}:123456789012:endpoint/{platform}/{name}/{id}".format( return "arn:aws:sns:{region}:{AccountId}:endpoint/{platform}/{name}/{id}".format(
region=self.region, region=self.region,
AccountId=DEFAULT_ACCOUNT_ID,
platform=self.application.platform, platform=self.application.platform,
name=self.application.name, name=self.application.name,
id=self.id, id=self.id,
@ -390,10 +397,6 @@ class SNSBackend(BaseBackend):
return self._get_values_nexttoken(self.topics, next_token) return self._get_values_nexttoken(self.topics, next_token)
def delete_topic(self, arn): def delete_topic(self, arn):
topic = self.get_topic(arn)
subscriptions = self._get_topic_subscriptions(topic)
for sub in subscriptions:
self.unsubscribe(sub.arn)
self.topics.pop(arn) self.topics.pop(arn)
def get_topic(self, arn): def get_topic(self, arn):
@ -432,11 +435,18 @@ class SNSBackend(BaseBackend):
subscription = Subscription(topic, endpoint, protocol) subscription = Subscription(topic, endpoint, protocol)
attributes = { attributes = {
"PendingConfirmation": "false", "PendingConfirmation": "false",
"ConfirmationWasAuthenticated": "true",
"Endpoint": endpoint, "Endpoint": endpoint,
"TopicArn": topic_arn, "TopicArn": topic_arn,
"Protocol": protocol, "Protocol": protocol,
"SubscriptionArn": subscription.arn, "SubscriptionArn": subscription.arn,
"Owner": DEFAULT_ACCOUNT_ID,
"RawMessageDelivery": "false",
} }
if protocol in ["http", "https"]:
attributes["EffectiveDeliveryPolicy"] = topic.effective_delivery_policy
subscription.attributes = attributes subscription.attributes = attributes
self.subscriptions[subscription.arn] = subscription self.subscriptions[subscription.arn] = subscription
return subscription return subscription
@ -452,7 +462,7 @@ class SNSBackend(BaseBackend):
return None return None
def unsubscribe(self, subscription_arn): def unsubscribe(self, subscription_arn):
self.subscriptions.pop(subscription_arn) self.subscriptions.pop(subscription_arn, None)
def list_subscriptions(self, topic_arn=None, next_token=None): def list_subscriptions(self, topic_arn=None, next_token=None):
if topic_arn: if topic_arn:
@ -693,21 +703,25 @@ class SNSBackend(BaseBackend):
sns_backends = {} sns_backends = {}
for region in Session().get_available_regions("sns"): for region in Session().get_available_regions("sns"):
sns_backends[region] = SNSBackend(region) sns_backends[region] = SNSBackend(region)
for region in Session().get_available_regions("sns", partition_name="aws-us-gov"):
sns_backends[region] = SNSBackend(region)
for region in Session().get_available_regions("sns", partition_name="aws-cn"):
sns_backends[region] = SNSBackend(region)
DEFAULT_EFFECTIVE_DELIVERY_POLICY = { DEFAULT_EFFECTIVE_DELIVERY_POLICY = {
"http": { "defaultHealthyRetryPolicy": {
"disableSubscriptionOverrides": False, "numNoDelayRetries": 0,
"defaultHealthyRetryPolicy": { "numMinDelayRetries": 0,
"numNoDelayRetries": 0, "minDelayTarget": 20,
"numMinDelayRetries": 0, "maxDelayTarget": 20,
"minDelayTarget": 20, "numMaxDelayRetries": 0,
"maxDelayTarget": 20, "numRetries": 3,
"numMaxDelayRetries": 0, "backoffFunction": "linear",
"numRetries": 3, },
"backoffFunction": "linear", "sicklyRetryPolicy": None,
}, "throttlePolicy": None,
} "guaranteed": False,
} }

View File

@ -8,7 +8,7 @@ import six
import struct import struct
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
import boto.sqs from boto3 import Session
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -32,7 +32,8 @@ from .exceptions import (
InvalidAttributeName, InvalidAttributeName,
) )
DEFAULT_ACCOUNT_ID = 123456789012 from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
DEFAULT_SENDER_ID = "AIDAIT2UOQQY3AUEKVGXU" DEFAULT_SENDER_ID = "AIDAIT2UOQQY3AUEKVGXU"
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
@ -417,8 +418,8 @@ class Queue(BaseModel):
return result return result
def url(self, request_url): def url(self, request_url):
return "{0}://{1}/123456789012/{2}".format( return "{0}://{1}/{2}/{3}".format(
request_url.scheme, request_url.netloc, self.name request_url.scheme, request_url.netloc, DEFAULT_ACCOUNT_ID, self.name
) )
@property @property
@ -856,5 +857,9 @@ class SQSBackend(BaseBackend):
sqs_backends = {} sqs_backends = {}
for region in boto.sqs.regions(): for region in Session().get_available_regions("sqs"):
sqs_backends[region.name] = SQSBackend(region.name) sqs_backends[region] = SQSBackend(region)
for region in Session().get_available_regions("sqs", partition_name="aws-us-gov"):
sqs_backends[region] = SQSBackend(region)
for region in Session().get_available_regions("sqs", partition_name="aws-cn"):
sqs_backends[region] = SQSBackend(region)

View File

@ -13,6 +13,7 @@ import time
import uuid import uuid
import itertools import itertools
from .utils import parameter_arn
from .exceptions import ( from .exceptions import (
ValidationException, ValidationException,
InvalidFilterValue, InvalidFilterValue,
@ -60,19 +61,23 @@ class Parameter(BaseModel):
if value.startswith(prefix): if value.startswith(prefix):
return value[len(prefix) :] return value[len(prefix) :]
def response_object(self, decrypt=False): def response_object(self, decrypt=False, region=None):
r = { r = {
"Name": self.name, "Name": self.name,
"Type": self.type, "Type": self.type,
"Value": self.decrypt(self.value) if decrypt else self.value, "Value": self.decrypt(self.value) if decrypt else self.value,
"Version": self.version, "Version": self.version,
"LastModifiedDate": round(self.last_modified_date, 3),
} }
if region:
r["ARN"] = parameter_arn(region, self.name)
return r return r
def describe_response_object(self, decrypt=False): def describe_response_object(self, decrypt=False):
r = self.response_object(decrypt) r = self.response_object(decrypt)
r["LastModifiedDate"] = int(self.last_modified_date) r["LastModifiedDate"] = round(self.last_modified_date, 3)
r["LastModifiedUser"] = "N/A" r["LastModifiedUser"] = "N/A"
if self.description: if self.description:
@ -510,7 +515,15 @@ class SimpleSystemManagerBackend(BaseBackend):
result.append(self.get_parameter(name, with_decryption)) result.append(self.get_parameter(name, with_decryption))
return result return result
def get_parameters_by_path(self, path, with_decryption, recursive, filters=None): def get_parameters_by_path(
self,
path,
with_decryption,
recursive,
filters=None,
next_token=None,
max_results=10,
):
"""Implement the get-parameters-by-path-API in the backend.""" """Implement the get-parameters-by-path-API in the backend."""
result = [] result = []
# path could be with or without a trailing /. we handle this # path could be with or without a trailing /. we handle this
@ -527,7 +540,19 @@ class SimpleSystemManagerBackend(BaseBackend):
continue continue
result.append(self.get_parameter(param_name, with_decryption)) result.append(self.get_parameter(param_name, with_decryption))
return result return self._get_values_nexttoken(result, max_results, next_token)
def _get_values_nexttoken(self, values_list, max_results, next_token=None):
if next_token is None:
next_token = 0
next_token = int(next_token)
max_results = int(max_results)
values = values_list[next_token : next_token + max_results]
if len(values) == max_results:
next_token = str(next_token + max_results)
else:
next_token = None
return values, next_token
def get_parameter_history(self, name, with_decryption): def get_parameter_history(self, name, with_decryption):
if name in self._parameters: if name in self._parameters:

View File

@ -51,7 +51,7 @@ class SimpleSystemManagerResponse(BaseResponse):
} }
return json.dumps(error), dict(status=400) return json.dumps(error), dict(status=400)
response = {"Parameter": result.response_object(with_decryption)} response = {"Parameter": result.response_object(with_decryption, self.region)}
return json.dumps(response) return json.dumps(response)
def get_parameters(self): def get_parameters(self):
@ -63,7 +63,7 @@ class SimpleSystemManagerResponse(BaseResponse):
response = {"Parameters": [], "InvalidParameters": []} response = {"Parameters": [], "InvalidParameters": []}
for parameter in result: for parameter in result:
param_data = parameter.response_object(with_decryption) param_data = parameter.response_object(with_decryption, self.region)
response["Parameters"].append(param_data) response["Parameters"].append(param_data)
param_names = [param.name for param in result] param_names = [param.name for param in result]
@ -77,15 +77,22 @@ class SimpleSystemManagerResponse(BaseResponse):
with_decryption = self._get_param("WithDecryption") with_decryption = self._get_param("WithDecryption")
recursive = self._get_param("Recursive", False) recursive = self._get_param("Recursive", False)
filters = self._get_param("ParameterFilters") filters = self._get_param("ParameterFilters")
token = self._get_param("NextToken")
max_results = self._get_param("MaxResults", 10)
result = self.ssm_backend.get_parameters_by_path( result, next_token = self.ssm_backend.get_parameters_by_path(
path, with_decryption, recursive, filters path,
with_decryption,
recursive,
filters,
next_token=token,
max_results=max_results,
) )
response = {"Parameters": []} response = {"Parameters": [], "NextToken": next_token}
for parameter in result: for parameter in result:
param_data = parameter.response_object(with_decryption) param_data = parameter.response_object(with_decryption, self.region)
response["Parameters"].append(param_data) response["Parameters"].append(param_data)
return json.dumps(response) return json.dumps(response)

9
moto/ssm/utils.py Normal file
View File

@ -0,0 +1,9 @@
ACCOUNT_ID = "1234567890"
def parameter_arn(region, parameter_name):
if parameter_name[0] == "/":
parameter_name = parameter_name[1:]
return "arn:aws:ssm:{0}:{1}:parameter/{2}".format(
region, ACCOUNT_ID, parameter_name
)

View File

@ -1,6 +1,8 @@
import boto
import re import re
from datetime import datetime from datetime import datetime
from boto3 import Session
from moto.core import BaseBackend from moto.core import BaseBackend
from moto.core.utils import iso_8601_datetime_without_milliseconds from moto.core.utils import iso_8601_datetime_without_milliseconds
from moto.sts.models import ACCOUNT_ID from moto.sts.models import ACCOUNT_ID
@ -280,7 +282,12 @@ class StepFunctionBackend(BaseBackend):
return ACCOUNT_ID return ACCOUNT_ID
stepfunction_backends = { stepfunction_backends = {}
_region.name: StepFunctionBackend(_region.name) for region in Session().get_available_regions("stepfunctions"):
for _region in boto.awslambda.regions() stepfunction_backends[region] = StepFunctionBackend(region)
} for region in Session().get_available_regions(
"stepfunctions", partition_name="aws-us-gov"
):
stepfunction_backends[region] = StepFunctionBackend(region)
for region in Session().get_available_regions("stepfunctions", partition_name="aws-cn"):
stepfunction_backends[region] = StepFunctionBackend(region)

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import datetime import datetime
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.iam.models import ACCOUNT_ID from moto.core import ACCOUNT_ID
from moto.sts.utils import ( from moto.sts.utils import (
random_access_key_id, random_access_key_id,
random_secret_access_key, random_secret_access_key,

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.iam.models import ACCOUNT_ID from moto.core import ACCOUNT_ID
from moto.iam import iam_backend from moto.iam import iam_backend
from .exceptions import STSValidationError from .exceptions import STSValidationError
from .models import sts_backend from .models import sts_backend

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import boto.swf from boto3 import Session
from moto.core import BaseBackend from moto.core import BaseBackend
@ -418,5 +418,9 @@ class SWFBackend(BaseBackend):
swf_backends = {} swf_backends = {}
for region in boto.swf.regions(): for region in Session().get_available_regions("swf"):
swf_backends[region.name] = SWFBackend(region.name) swf_backends[region] = SWFBackend(region)
for region in Session().get_available_regions("swf", partition_name="aws-us-gov"):
swf_backends[region] = SWFBackend(region)
for region in Session().get_available_regions("swf", partition_name="aws-cn"):
swf_backends[region] = SWFBackend(region)

View File

@ -2,8 +2,9 @@
mock mock
nose nose
black; python_version >= '3.6' black; python_version >= '3.6'
regex==2019.11.1; python_version >= '3.6' # Needed for black
sure==1.4.11 sure==1.4.11
coverage coverage==4.5.4
flake8==3.7.8 flake8==3.7.8
freezegun freezegun
flask flask

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import boto3 from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -16,5 +16,10 @@ class {{ service_class }}Backend(BaseBackend):
# add methods from here # add methods from here
available_regions = boto3.session.Session().get_available_regions("{{ service }}") {{ escaped_service }}_backends = {}
{{ escaped_service }}_backends = {region: {{ service_class }}Backend(region) for region in available_regions} for region in Session().get_available_regions("{{ service }}"):
{{ escaped_service }}_backends[region] = {{ service_class }}Backend()
for region in Session().get_available_regions("{{ service }}", partition_name="aws-us-gov"):
{{ escaped_service }}_backends[region] = {{ service_class }}Backend()
for region in Session().get_available_regions("{{ service }}", partition_name="aws-cn"):
{{ escaped_service }}_backends[region] = {{ service_class }}Backend()

View File

@ -9,6 +9,7 @@ import uuid
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
from moto import mock_acm from moto import mock_acm
from moto.core import ACCOUNT_ID
RESOURCE_FOLDER = os.path.join(os.path.dirname(__file__), "resources") RESOURCE_FOLDER = os.path.join(os.path.dirname(__file__), "resources")
@ -19,7 +20,9 @@ SERVER_CRT = _GET_RESOURCE("star_moto_com.pem")
SERVER_COMMON_NAME = "*.moto.com" SERVER_COMMON_NAME = "*.moto.com"
SERVER_CRT_BAD = _GET_RESOURCE("star_moto_com-bad.pem") SERVER_CRT_BAD = _GET_RESOURCE("star_moto_com-bad.pem")
SERVER_KEY = _GET_RESOURCE("star_moto_com.key") SERVER_KEY = _GET_RESOURCE("star_moto_com.key")
BAD_ARN = "arn:aws:acm:us-east-2:123456789012:certificate/_0000000-0000-0000-0000-000000000000" BAD_ARN = "arn:aws:acm:us-east-2:{}:certificate/_0000000-0000-0000-0000-000000000000".format(
ACCOUNT_ID
)
def _import_cert(client): def _import_cert(client):

View File

@ -9,6 +9,7 @@ from botocore.exceptions import ClientError
import responses import responses
from moto import mock_apigateway, settings from moto import mock_apigateway, settings
from moto.core import ACCOUNT_ID
from nose.tools import assert_raises from nose.tools import assert_raises
@ -881,7 +882,9 @@ def test_put_integration_validation():
client.put_integration( client.put_integration(
restApiId=api_id, restApiId=api_id,
resourceId=root_id, resourceId=root_id,
credentials="arn:aws:iam::123456789012:role/service-role/testfunction-role-oe783psq", credentials="arn:aws:iam::{}:role/service-role/testfunction-role-oe783psq".format(
ACCOUNT_ID
),
httpMethod="GET", httpMethod="GET",
type=type, type=type,
uri="arn:aws:apigateway:us-west-2:s3:path/b/k", uri="arn:aws:apigateway:us-west-2:s3:path/b/k",
@ -903,7 +906,9 @@ def test_put_integration_validation():
client.put_integration( client.put_integration(
restApiId=api_id, restApiId=api_id,
resourceId=root_id, resourceId=root_id,
credentials="arn:aws:iam::123456789012:role/service-role/testfunction-role-oe783psq", credentials="arn:aws:iam::{}:role/service-role/testfunction-role-oe783psq".format(
ACCOUNT_ID
),
httpMethod="GET", httpMethod="GET",
type=type, type=type,
uri="arn:aws:apigateway:us-west-2:s3:path/b/k", uri="arn:aws:apigateway:us-west-2:s3:path/b/k",
@ -1130,6 +1135,23 @@ def test_http_proxying_integration():
requests.get(deploy_url).content.should.equal(b"a fake response") requests.get(deploy_url).content.should.equal(b"a fake response")
@mock_apigateway
def test_create_api_key():
region_name = "us-west-2"
client = boto3.client("apigateway", region_name=region_name)
apikey_value = "12345"
apikey_name = "TESTKEY1"
payload = {"value": apikey_value, "name": apikey_name}
client.create_api_key(**payload)
response = client.get_api_keys()
len(response["items"]).should.equal(1)
client.create_api_key.when.called_with(**payload).should.throw(ClientError)
@mock_apigateway @mock_apigateway
def test_api_keys(): def test_api_keys():
region_name = "us-west-2" region_name = "us-west-2"
@ -1139,26 +1161,18 @@ def test_api_keys():
apikey_value = "12345" apikey_value = "12345"
apikey_name = "TESTKEY1" apikey_name = "TESTKEY1"
payload = {"value": apikey_value, "name": apikey_name} payload = {
"value": apikey_value,
"name": apikey_name,
"tags": {"tag1": "test_tag1", "tag2": "1"},
}
response = client.create_api_key(**payload) response = client.create_api_key(**payload)
apikey_id = response["id"]
apikey = client.get_api_key(apiKey=response["id"]) apikey = client.get_api_key(apiKey=response["id"])
apikey["name"].should.equal(apikey_name) apikey["name"].should.equal(apikey_name)
apikey["value"].should.equal(apikey_value) apikey["value"].should.equal(apikey_value)
apikey_name = "TESTKEY2"
payload = {"name": apikey_name, "tags": {"tag1": "test_tag1", "tag2": "1"}}
response = client.create_api_key(**payload)
apikey_id = response["id"]
apikey = client.get_api_key(apiKey=apikey_id)
apikey["name"].should.equal(apikey_name)
apikey["tags"]["tag1"].should.equal("test_tag1") apikey["tags"]["tag1"].should.equal("test_tag1")
apikey["tags"]["tag2"].should.equal("1") apikey["tags"]["tag2"].should.equal("1")
len(apikey["value"]).should.equal(40)
apikey_name = "TESTKEY3"
payload = {"name": apikey_name}
response = client.create_api_key(**payload)
apikey_id = response["id"]
patch_operations = [ patch_operations = [
{"op": "replace", "path": "/name", "value": "TESTKEY3_CHANGE"}, {"op": "replace", "path": "/name", "value": "TESTKEY3_CHANGE"},
@ -1172,13 +1186,25 @@ def test_api_keys():
response["description"].should.equal("APIKEY UPDATE TEST") response["description"].should.equal("APIKEY UPDATE TEST")
response["enabled"].should.equal(False) response["enabled"].should.equal(False)
updated_api_key = client.get_api_key(apiKey=apikey_id)
updated_api_key["name"].should.equal("TESTKEY3_CHANGE")
updated_api_key["customerId"].should.equal("12345")
updated_api_key["description"].should.equal("APIKEY UPDATE TEST")
updated_api_key["enabled"].should.equal(False)
response = client.get_api_keys() response = client.get_api_keys()
len(response["items"]).should.equal(3) len(response["items"]).should.equal(1)
payload = {"name": apikey_name}
client.create_api_key(**payload)
response = client.get_api_keys()
len(response["items"]).should.equal(2)
client.delete_api_key(apiKey=apikey_id) client.delete_api_key(apiKey=apikey_id)
response = client.get_api_keys() response = client.get_api_keys()
len(response["items"]).should.equal(2) len(response["items"]).should.equal(1)
@mock_apigateway @mock_apigateway

View File

@ -8,6 +8,7 @@ import sure # noqa
from moto import mock_autoscaling_deprecated from moto import mock_autoscaling_deprecated
from moto import mock_autoscaling from moto import mock_autoscaling
from moto.core import ACCOUNT_ID
from tests.helpers import requires_boto_gte from tests.helpers import requires_boto_gte
@ -22,7 +23,9 @@ def test_create_launch_configuration():
security_groups=["default", "default2"], security_groups=["default", "default2"],
user_data=b"This is some user_data", user_data=b"This is some user_data",
instance_monitoring=True, instance_monitoring=True,
instance_profile_name="arn:aws:iam::123456789012:instance-profile/testing", instance_profile_name="arn:aws:iam::{}:instance-profile/testing".format(
ACCOUNT_ID
),
spot_price=0.1, spot_price=0.1,
) )
conn.create_launch_configuration(config) conn.create_launch_configuration(config)
@ -36,7 +39,7 @@ def test_create_launch_configuration():
launch_config.user_data.should.equal(b"This is some user_data") launch_config.user_data.should.equal(b"This is some user_data")
launch_config.instance_monitoring.enabled.should.equal("true") launch_config.instance_monitoring.enabled.should.equal("true")
launch_config.instance_profile_name.should.equal( launch_config.instance_profile_name.should.equal(
"arn:aws:iam::123456789012:instance-profile/testing" "arn:aws:iam::{}:instance-profile/testing".format(ACCOUNT_ID)
) )
launch_config.spot_price.should.equal(0.1) launch_config.spot_price.should.equal(0.1)
@ -71,7 +74,9 @@ def test_create_launch_configuration_with_block_device_mappings():
security_groups=["default", "default2"], security_groups=["default", "default2"],
user_data=b"This is some user_data", user_data=b"This is some user_data",
instance_monitoring=True, instance_monitoring=True,
instance_profile_name="arn:aws:iam::123456789012:instance-profile/testing", instance_profile_name="arn:aws:iam::{}:instance-profile/testing".format(
ACCOUNT_ID
),
spot_price=0.1, spot_price=0.1,
block_device_mappings=[block_device_mapping], block_device_mappings=[block_device_mapping],
) )
@ -86,7 +91,7 @@ def test_create_launch_configuration_with_block_device_mappings():
launch_config.user_data.should.equal(b"This is some user_data") launch_config.user_data.should.equal(b"This is some user_data")
launch_config.instance_monitoring.enabled.should.equal("true") launch_config.instance_monitoring.enabled.should.equal("true")
launch_config.instance_profile_name.should.equal( launch_config.instance_profile_name.should.equal(
"arn:aws:iam::123456789012:instance-profile/testing" "arn:aws:iam::{}:instance-profile/testing".format(ACCOUNT_ID)
) )
launch_config.spot_price.should.equal(0.1) launch_config.spot_price.should.equal(0.1)
len(launch_config.block_device_mappings).should.equal(3) len(launch_config.block_device_mappings).should.equal(3)

View File

@ -306,8 +306,8 @@ def test_create_function_from_aws_bucket():
result.should.equal( result.should.equal(
{ {
"FunctionName": "testFunction", "FunctionName": "testFunction",
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format( "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
_lambda_region _lambda_region, ACCOUNT_ID
), ),
"Runtime": "python2.7", "Runtime": "python2.7",
"Role": result["Role"], "Role": result["Role"],
@ -353,8 +353,8 @@ def test_create_function_from_zipfile():
result.should.equal( result.should.equal(
{ {
"FunctionName": "testFunction", "FunctionName": "testFunction",
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format( "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
_lambda_region _lambda_region, ACCOUNT_ID
), ),
"Runtime": "python2.7", "Runtime": "python2.7",
"Role": result["Role"], "Role": result["Role"],
@ -431,7 +431,7 @@ def test_get_function():
result = conn.get_function(FunctionName="testFunction", Qualifier="$LATEST") result = conn.get_function(FunctionName="testFunction", Qualifier="$LATEST")
result["Configuration"]["Version"].should.equal("$LATEST") result["Configuration"]["Version"].should.equal("$LATEST")
result["Configuration"]["FunctionArn"].should.equal( result["Configuration"]["FunctionArn"].should.equal(
"arn:aws:lambda:us-west-2:123456789012:function:testFunction:$LATEST" "arn:aws:lambda:us-west-2:{}:function:testFunction:$LATEST".format(ACCOUNT_ID)
) )
# Test get function when can't find function name # Test get function when can't find function name
@ -620,8 +620,8 @@ def test_list_create_list_get_delete_list():
"CodeSha256": hashlib.sha256(zip_content).hexdigest(), "CodeSha256": hashlib.sha256(zip_content).hexdigest(),
"CodeSize": len(zip_content), "CodeSize": len(zip_content),
"Description": "test lambda function", "Description": "test lambda function",
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format( "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
_lambda_region _lambda_region, ACCOUNT_ID
), ),
"FunctionName": "testFunction", "FunctionName": "testFunction",
"Handler": "lambda_function.lambda_handler", "Handler": "lambda_function.lambda_handler",
@ -749,16 +749,17 @@ def test_tags_not_found():
""" """
conn = boto3.client("lambda", "us-west-2") conn = boto3.client("lambda", "us-west-2")
conn.list_tags.when.called_with( conn.list_tags.when.called_with(
Resource="arn:aws:lambda:123456789012:function:not-found" Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID)
).should.throw(botocore.client.ClientError) ).should.throw(botocore.client.ClientError)
conn.tag_resource.when.called_with( conn.tag_resource.when.called_with(
Resource="arn:aws:lambda:123456789012:function:not-found", Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
Tags=dict(spam="eggs"), Tags=dict(spam="eggs"),
).should.throw(botocore.client.ClientError) ).should.throw(botocore.client.ClientError)
conn.untag_resource.when.called_with( conn.untag_resource.when.called_with(
Resource="arn:aws:lambda:123456789012:function:not-found", TagKeys=["spam"] Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
TagKeys=["spam"],
).should.throw(botocore.client.ClientError) ).should.throw(botocore.client.ClientError)
@ -815,8 +816,8 @@ def test_get_function_created_with_zipfile():
"CodeSha256": hashlib.sha256(zip_content).hexdigest(), "CodeSha256": hashlib.sha256(zip_content).hexdigest(),
"CodeSize": len(zip_content), "CodeSize": len(zip_content),
"Description": "test lambda function", "Description": "test lambda function",
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format( "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
_lambda_region _lambda_region, ACCOUNT_ID
), ),
"FunctionName": "testFunction", "FunctionName": "testFunction",
"Handler": "lambda_function.handler", "Handler": "lambda_function.handler",
@ -921,18 +922,15 @@ def test_list_versions_by_function():
assert res["ResponseMetadata"]["HTTPStatusCode"] == 201 assert res["ResponseMetadata"]["HTTPStatusCode"] == 201
versions = conn.list_versions_by_function(FunctionName="testFunction") versions = conn.list_versions_by_function(FunctionName="testFunction")
assert len(versions["Versions"]) == 3 assert len(versions["Versions"]) == 3
assert ( assert versions["Versions"][0][
versions["Versions"][0]["FunctionArn"] "FunctionArn"
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction:$LATEST" ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:$LATEST".format(ACCOUNT_ID)
) assert versions["Versions"][1][
assert ( "FunctionArn"
versions["Versions"][1]["FunctionArn"] ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:1".format(ACCOUNT_ID)
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction:1" assert versions["Versions"][2][
) "FunctionArn"
assert ( ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:2".format(ACCOUNT_ID)
versions["Versions"][2]["FunctionArn"]
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction:2"
)
conn.create_function( conn.create_function(
FunctionName="testFunction_2", FunctionName="testFunction_2",
@ -947,9 +945,10 @@ def test_list_versions_by_function():
) )
versions = conn.list_versions_by_function(FunctionName="testFunction_2") versions = conn.list_versions_by_function(FunctionName="testFunction_2")
assert len(versions["Versions"]) == 1 assert len(versions["Versions"]) == 1
assert ( assert versions["Versions"][0][
versions["Versions"][0]["FunctionArn"] "FunctionArn"
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction_2:$LATEST" ] == "arn:aws:lambda:us-west-2:{}:function:testFunction_2:$LATEST".format(
ACCOUNT_ID
) )
@ -1426,8 +1425,8 @@ def test_update_function_zip():
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(), "CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
"CodeSize": len(zip_content_two), "CodeSize": len(zip_content_two),
"Description": "test lambda function", "Description": "test lambda function",
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunctionZip:2".format( "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionZip:2".format(
_lambda_region _lambda_region, ACCOUNT_ID
), ),
"FunctionName": "testFunctionZip", "FunctionName": "testFunctionZip",
"Handler": "lambda_function.lambda_handler", "Handler": "lambda_function.lambda_handler",
@ -1488,8 +1487,8 @@ def test_update_function_s3():
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(), "CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
"CodeSize": len(zip_content_two), "CodeSize": len(zip_content_two),
"Description": "test lambda function", "Description": "test lambda function",
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunctionS3:2".format( "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionS3:2".format(
_lambda_region _lambda_region, ACCOUNT_ID
), ),
"FunctionName": "testFunctionS3", "FunctionName": "testFunctionS3",
"Handler": "lambda_function.lambda_handler", "Handler": "lambda_function.lambda_handler",

View File

@ -55,6 +55,10 @@ def _setup(ec2_client, iam_client):
RoleName="TestRole", AssumeRolePolicyDocument="some_policy" RoleName="TestRole", AssumeRolePolicyDocument="some_policy"
) )
iam_arn = resp["Role"]["Arn"] iam_arn = resp["Role"]["Arn"]
iam_client.create_instance_profile(InstanceProfileName="TestRole")
iam_client.add_role_to_instance_profile(
InstanceProfileName="TestRole", RoleName="TestRole"
)
return vpc_id, subnet_id, sg_id, iam_arn return vpc_id, subnet_id, sg_id, iam_arn
@ -83,7 +87,7 @@ def test_create_managed_compute_environment():
"subnets": [subnet_id], "subnets": [subnet_id],
"securityGroupIds": [sg_id], "securityGroupIds": [sg_id],
"ec2KeyPair": "string", "ec2KeyPair": "string",
"instanceRole": iam_arn, "instanceRole": iam_arn.replace("role", "instance-profile"),
"tags": {"string": "string"}, "tags": {"string": "string"},
"bidPercentage": 123, "bidPercentage": 123,
"spotIamFleetRole": "string", "spotIamFleetRole": "string",
@ -209,7 +213,7 @@ def test_delete_managed_compute_environment():
"subnets": [subnet_id], "subnets": [subnet_id],
"securityGroupIds": [sg_id], "securityGroupIds": [sg_id],
"ec2KeyPair": "string", "ec2KeyPair": "string",
"instanceRole": iam_arn, "instanceRole": iam_arn.replace("role", "instance-profile"),
"tags": {"string": "string"}, "tags": {"string": "string"},
"bidPercentage": 123, "bidPercentage": 123,
"spotIamFleetRole": "string", "spotIamFleetRole": "string",
@ -608,6 +612,9 @@ def test_describe_task_definition():
resp = batch_client.describe_job_definitions(jobDefinitions=["sleep10", "test1"]) resp = batch_client.describe_job_definitions(jobDefinitions=["sleep10", "test1"])
len(resp["jobDefinitions"]).should.equal(3) len(resp["jobDefinitions"]).should.equal(3)
for job_definition in resp["jobDefinitions"]:
job_definition["status"].should.equal("ACTIVE")
@mock_logs @mock_logs
@mock_ec2 @mock_ec2

View File

@ -51,6 +51,10 @@ def _setup(ec2_client, iam_client):
RoleName="TestRole", AssumeRolePolicyDocument="some_policy" RoleName="TestRole", AssumeRolePolicyDocument="some_policy"
) )
iam_arn = resp["Role"]["Arn"] iam_arn = resp["Role"]["Arn"]
iam_client.create_instance_profile(InstanceProfileName="TestRole")
iam_client.add_role_to_instance_profile(
InstanceProfileName="TestRole", RoleName="TestRole"
)
return vpc_id, subnet_id, sg_id, iam_arn return vpc_id, subnet_id, sg_id, iam_arn
@ -78,7 +82,7 @@ def test_create_env_cf():
"InstanceTypes": ["optimal"], "InstanceTypes": ["optimal"],
"Subnets": [subnet_id], "Subnets": [subnet_id],
"SecurityGroupIds": [sg_id], "SecurityGroupIds": [sg_id],
"InstanceRole": iam_arn, "InstanceRole": iam_arn.replace("role", "instance-profile"),
}, },
"ServiceRole": iam_arn, "ServiceRole": iam_arn,
}, },
@ -129,7 +133,7 @@ def test_create_job_queue_cf():
"InstanceTypes": ["optimal"], "InstanceTypes": ["optimal"],
"Subnets": [subnet_id], "Subnets": [subnet_id],
"SecurityGroupIds": [sg_id], "SecurityGroupIds": [sg_id],
"InstanceRole": iam_arn, "InstanceRole": iam_arn.replace("role", "instance-profile"),
}, },
"ServiceRole": iam_arn, "ServiceRole": iam_arn,
}, },
@ -195,7 +199,7 @@ def test_create_job_def_cf():
"InstanceTypes": ["optimal"], "InstanceTypes": ["optimal"],
"Subnets": [subnet_id], "Subnets": [subnet_id],
"SecurityGroupIds": [sg_id], "SecurityGroupIds": [sg_id],
"InstanceRole": iam_arn, "InstanceRole": iam_arn.replace("role", "instance-profile"),
}, },
"ServiceRole": iam_arn, "ServiceRole": iam_arn,
}, },

View File

@ -14,6 +14,7 @@ import sure # noqa
# Ensure 'assert_raises' context manager support for Python 2.6 # Ensure 'assert_raises' context manager support for Python 2.6
import tests.backport_assert_raises # noqa import tests.backport_assert_raises # noqa
from nose.tools import assert_raises from nose.tools import assert_raises
from moto.core import ACCOUNT_ID
from moto import ( from moto import (
mock_cloudformation_deprecated, mock_cloudformation_deprecated,
@ -129,12 +130,12 @@ def test_create_stack_with_notification_arn():
conn.create_stack( conn.create_stack(
"test_stack_with_notifications", "test_stack_with_notifications",
template_body=dummy_template_json, template_body=dummy_template_json,
notification_arns="arn:aws:sns:us-east-1:123456789012:fake-queue", notification_arns="arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID),
) )
stack = conn.describe_stacks()[0] stack = conn.describe_stacks()[0]
[n.value for n in stack.notification_arns].should.contain( [n.value for n in stack.notification_arns].should.contain(
"arn:aws:sns:us-east-1:123456789012:fake-queue" "arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID)
) )

View File

@ -11,6 +11,7 @@ import sure # noqa
from nose.tools import assert_raises from nose.tools import assert_raises
from moto import mock_cloudformation, mock_s3, mock_sqs, mock_ec2 from moto import mock_cloudformation, mock_s3, mock_sqs, mock_ec2
from moto.core import ACCOUNT_ID
dummy_template = { dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09", "AWSTemplateFormatVersion": "2010-09-09",
@ -174,17 +175,17 @@ def test_boto3_describe_stack_instances():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"], Regions=["us-east-1", "us-west-2"],
) )
usw2_instance = cf_conn.describe_stack_instance( usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set", StackSetName="test_stack_set",
StackInstanceAccount="123456789012", StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-2", StackInstanceRegion="us-west-2",
) )
use1_instance = cf_conn.describe_stack_instance( use1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set", StackSetName="test_stack_set",
StackInstanceAccount="123456789012", StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-east-1", StackInstanceRegion="us-east-1",
) )
@ -192,13 +193,13 @@ def test_boto3_describe_stack_instances():
"us-west-2" "us-west-2"
) )
usw2_instance["StackInstance"].should.have.key("Account").which.should.equal( usw2_instance["StackInstance"].should.have.key("Account").which.should.equal(
"123456789012" ACCOUNT_ID
) )
use1_instance["StackInstance"].should.have.key("Region").which.should.equal( use1_instance["StackInstance"].should.have.key("Region").which.should.equal(
"us-east-1" "us-east-1"
) )
use1_instance["StackInstance"].should.have.key("Account").which.should.equal( use1_instance["StackInstance"].should.have.key("Account").which.should.equal(
"123456789012" ACCOUNT_ID
) )
@ -236,7 +237,7 @@ def test_boto3_stop_stack_set_operation():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"], Regions=["us-east-1", "us-west-1", "us-west-2"],
) )
operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[ operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[
@ -257,7 +258,7 @@ def test_boto3_describe_stack_set_operation():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"], Regions=["us-east-1", "us-west-1", "us-west-2"],
) )
operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[ operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[
@ -282,7 +283,7 @@ def test_boto3_list_stack_set_operation_results():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"], Regions=["us-east-1", "us-west-1", "us-west-2"],
) )
operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[ operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[
@ -297,9 +298,7 @@ def test_boto3_list_stack_set_operation_results():
) )
response["Summaries"].should.have.length_of(3) response["Summaries"].should.have.length_of(3)
response["Summaries"][0].should.have.key("Account").which.should.equal( response["Summaries"][0].should.have.key("Account").which.should.equal(ACCOUNT_ID)
"123456789012"
)
response["Summaries"][1].should.have.key("Status").which.should.equal("STOPPED") response["Summaries"][1].should.have.key("Status").which.should.equal("STOPPED")
@ -321,28 +320,28 @@ def test_boto3_update_stack_instances():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"], Regions=["us-east-1", "us-west-1", "us-west-2"],
) )
cf_conn.update_stack_instances( cf_conn.update_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-west-1", "us-west-2"], Regions=["us-west-1", "us-west-2"],
ParameterOverrides=param_overrides, ParameterOverrides=param_overrides,
) )
usw2_instance = cf_conn.describe_stack_instance( usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set", StackSetName="test_stack_set",
StackInstanceAccount="123456789012", StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-2", StackInstanceRegion="us-west-2",
) )
usw1_instance = cf_conn.describe_stack_instance( usw1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set", StackSetName="test_stack_set",
StackInstanceAccount="123456789012", StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-1", StackInstanceRegion="us-west-1",
) )
use1_instance = cf_conn.describe_stack_instance( use1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set", StackSetName="test_stack_set",
StackInstanceAccount="123456789012", StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-east-1", StackInstanceRegion="us-east-1",
) )
@ -383,13 +382,13 @@ def test_boto3_delete_stack_instances():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"], Regions=["us-east-1", "us-west-2"],
) )
cf_conn.delete_stack_instances( cf_conn.delete_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1"], Regions=["us-east-1"],
RetainStacks=False, RetainStacks=False,
) )
@ -410,7 +409,7 @@ def test_boto3_create_stack_instances():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"], Regions=["us-east-1", "us-west-2"],
) )
@ -419,7 +418,7 @@ def test_boto3_create_stack_instances():
].should.have.length_of(2) ].should.have.length_of(2)
cf_conn.list_stack_instances(StackSetName="test_stack_set")["Summaries"][0][ cf_conn.list_stack_instances(StackSetName="test_stack_set")["Summaries"][0][
"Account" "Account"
].should.equal("123456789012") ].should.equal(ACCOUNT_ID)
@mock_cloudformation @mock_cloudformation
@ -440,13 +439,13 @@ def test_boto3_create_stack_instances_with_param_overrides():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"], Regions=["us-east-1", "us-west-2"],
ParameterOverrides=param_overrides, ParameterOverrides=param_overrides,
) )
usw2_instance = cf_conn.describe_stack_instance( usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set", StackSetName="test_stack_set",
StackInstanceAccount="123456789012", StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-2", StackInstanceRegion="us-west-2",
) )
@ -509,12 +508,12 @@ def test_boto3_list_stack_set_operations():
) )
cf_conn.create_stack_instances( cf_conn.create_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"], Regions=["us-east-1", "us-west-2"],
) )
cf_conn.update_stack_instances( cf_conn.update_stack_instances(
StackSetName="test_stack_set", StackSetName="test_stack_set",
Accounts=["123456789012"], Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"], Regions=["us-east-1", "us-west-2"],
) )
@ -682,12 +681,12 @@ def test_create_stack_with_notification_arn():
cf.create_stack( cf.create_stack(
StackName="test_stack_with_notifications", StackName="test_stack_with_notifications",
TemplateBody=dummy_template_json, TemplateBody=dummy_template_json,
NotificationARNs=["arn:aws:sns:us-east-1:123456789012:fake-queue"], NotificationARNs=["arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID)],
) )
stack = list(cf.stacks.all())[0] stack = list(cf.stacks.all())[0]
stack.notification_arns.should.contain( stack.notification_arns.should.contain(
"arn:aws:sns:us-east-1:123456789012:fake-queue" "arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID)
) )
@ -697,10 +696,10 @@ def test_create_stack_with_role_arn():
cf.create_stack( cf.create_stack(
StackName="test_stack_with_notifications", StackName="test_stack_with_notifications",
TemplateBody=dummy_template_json, TemplateBody=dummy_template_json,
RoleARN="arn:aws:iam::123456789012:role/moto", RoleARN="arn:aws:iam::{}:role/moto".format(ACCOUNT_ID),
) )
stack = list(cf.stacks.all())[0] stack = list(cf.stacks.all())[0]
stack.role_arn.should.equal("arn:aws:iam::123456789012:role/moto") stack.role_arn.should.equal("arn:aws:iam::{}:role/moto".format(ACCOUNT_ID))
@mock_cloudformation @mock_cloudformation
@ -1019,7 +1018,7 @@ def test_describe_updated_stack():
cf_conn.update_stack( cf_conn.update_stack(
StackName="test_stack", StackName="test_stack",
RoleARN="arn:aws:iam::123456789012:role/moto", RoleARN="arn:aws:iam::{}:role/moto".format(ACCOUNT_ID),
TemplateBody=dummy_update_template_json, TemplateBody=dummy_update_template_json,
Tags=[{"Key": "foo", "Value": "baz"}], Tags=[{"Key": "foo", "Value": "baz"}],
) )
@ -1030,7 +1029,7 @@ def test_describe_updated_stack():
stack_by_id["StackId"].should.equal(stack["StackId"]) stack_by_id["StackId"].should.equal(stack["StackId"])
stack_by_id["StackName"].should.equal("test_stack") stack_by_id["StackName"].should.equal("test_stack")
stack_by_id["StackStatus"].should.equal("UPDATE_COMPLETE") stack_by_id["StackStatus"].should.equal("UPDATE_COMPLETE")
stack_by_id["RoleARN"].should.equal("arn:aws:iam::123456789012:role/moto") stack_by_id["RoleARN"].should.equal("arn:aws:iam::{}:role/moto".format(ACCOUNT_ID))
stack_by_id["Tags"].should.equal([{"Key": "foo", "Value": "baz"}]) stack_by_id["Tags"].should.equal([{"Key": "foo", "Value": "baz"}])

View File

@ -43,6 +43,7 @@ from moto import (
mock_sqs_deprecated, mock_sqs_deprecated,
mock_elbv2, mock_elbv2,
) )
from moto.core import ACCOUNT_ID
from moto.dynamodb2.models import Table from moto.dynamodb2.models import Table
from .fixtures import ( from .fixtures import (
@ -1912,7 +1913,7 @@ def test_stack_spot_fleet():
"Type": "AWS::EC2::SpotFleet", "Type": "AWS::EC2::SpotFleet",
"Properties": { "Properties": {
"SpotFleetRequestConfigData": { "SpotFleetRequestConfigData": {
"IamFleetRole": "arn:aws:iam::123456789012:role/fleet", "IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
"SpotPrice": "0.12", "SpotPrice": "0.12",
"TargetCapacity": 6, "TargetCapacity": 6,
"AllocationStrategy": "diversified", "AllocationStrategy": "diversified",
@ -1933,7 +1934,9 @@ def test_stack_spot_fleet():
"SecurityGroups": [{"GroupId": "sg-123"}], "SecurityGroups": [{"GroupId": "sg-123"}],
"SubnetId": subnet_id, "SubnetId": subnet_id,
"IamInstanceProfile": { "IamInstanceProfile": {
"Arn": "arn:aws:iam::123456789012:role/fleet" "Arn": "arn:aws:iam::{}:role/fleet".format(
ACCOUNT_ID
)
}, },
"WeightedCapacity": "4", "WeightedCapacity": "4",
"SpotPrice": "10.00", "SpotPrice": "10.00",
@ -1966,7 +1969,7 @@ def test_stack_spot_fleet():
spot_fleet_config["SpotPrice"].should.equal("0.12") spot_fleet_config["SpotPrice"].should.equal("0.12")
spot_fleet_config["TargetCapacity"].should.equal(6) spot_fleet_config["TargetCapacity"].should.equal(6)
spot_fleet_config["IamFleetRole"].should.equal( spot_fleet_config["IamFleetRole"].should.equal(
"arn:aws:iam::123456789012:role/fleet" "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID)
) )
spot_fleet_config["AllocationStrategy"].should.equal("diversified") spot_fleet_config["AllocationStrategy"].should.equal("diversified")
spot_fleet_config["FulfilledCapacity"].should.equal(6.0) spot_fleet_config["FulfilledCapacity"].should.equal(6.0)
@ -1999,7 +2002,7 @@ def test_stack_spot_fleet_should_figure_out_default_price():
"Type": "AWS::EC2::SpotFleet", "Type": "AWS::EC2::SpotFleet",
"Properties": { "Properties": {
"SpotFleetRequestConfigData": { "SpotFleetRequestConfigData": {
"IamFleetRole": "arn:aws:iam::123456789012:role/fleet", "IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
"TargetCapacity": 6, "TargetCapacity": 6,
"AllocationStrategy": "diversified", "AllocationStrategy": "diversified",
"LaunchSpecifications": [ "LaunchSpecifications": [
@ -2018,7 +2021,9 @@ def test_stack_spot_fleet_should_figure_out_default_price():
"SecurityGroups": [{"GroupId": "sg-123"}], "SecurityGroups": [{"GroupId": "sg-123"}],
"SubnetId": subnet_id, "SubnetId": subnet_id,
"IamInstanceProfile": { "IamInstanceProfile": {
"Arn": "arn:aws:iam::123456789012:role/fleet" "Arn": "arn:aws:iam::{}:role/fleet".format(
ACCOUNT_ID
)
}, },
"WeightedCapacity": "4", "WeightedCapacity": "4",
}, },

View File

@ -0,0 +1,222 @@
import boto3
import sure # noqa
from moto import mock_codecommit
from moto.iam.models import ACCOUNT_ID
from botocore.exceptions import ClientError
from nose.tools import assert_raises
@mock_codecommit
def test_create_repository():
client = boto3.client("codecommit", region_name="eu-central-1")
response = client.create_repository(
repositoryName="repository_one", repositoryDescription="description repo one"
)
response.should_not.be.none
response["repositoryMetadata"].should_not.be.none
response["repositoryMetadata"]["creationDate"].should_not.be.none
response["repositoryMetadata"]["lastModifiedDate"].should_not.be.none
response["repositoryMetadata"]["repositoryId"].should_not.be.empty
response["repositoryMetadata"]["repositoryName"].should.equal("repository_one")
response["repositoryMetadata"]["repositoryDescription"].should.equal(
"description repo one"
)
response["repositoryMetadata"]["cloneUrlSsh"].should.equal(
"ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
"eu-central-1", "repository_one"
)
)
response["repositoryMetadata"]["cloneUrlHttp"].should.equal(
"https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
"eu-central-1", "repository_one"
)
)
response["repositoryMetadata"]["Arn"].should.equal(
"arn:aws:codecommit:{0}:{1}:{2}".format(
"eu-central-1", ACCOUNT_ID, "repository_one"
)
)
response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID)
@mock_codecommit
def test_create_repository_without_description():
client = boto3.client("codecommit", region_name="eu-central-1")
response = client.create_repository(repositoryName="repository_two")
response.should_not.be.none
response.get("repositoryMetadata").should_not.be.none
response.get("repositoryMetadata").get("repositoryName").should.equal(
"repository_two"
)
response.get("repositoryMetadata").get("repositoryDescription").should.be.none
response["repositoryMetadata"].should_not.be.none
response["repositoryMetadata"]["creationDate"].should_not.be.none
response["repositoryMetadata"]["lastModifiedDate"].should_not.be.none
response["repositoryMetadata"]["repositoryId"].should_not.be.empty
response["repositoryMetadata"]["cloneUrlSsh"].should.equal(
"ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
"eu-central-1", "repository_two"
)
)
response["repositoryMetadata"]["cloneUrlHttp"].should.equal(
"https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
"eu-central-1", "repository_two"
)
)
response["repositoryMetadata"]["Arn"].should.equal(
"arn:aws:codecommit:{0}:{1}:{2}".format(
"eu-central-1", ACCOUNT_ID, "repository_two"
)
)
response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID)
@mock_codecommit
def test_create_repository_repository_name_exists():
client = boto3.client("codecommit", region_name="eu-central-1")
client.create_repository(repositoryName="repository_two")
with assert_raises(ClientError) as e:
client.create_repository(
repositoryName="repository_two",
repositoryDescription="description repo two",
)
ex = e.exception
ex.operation_name.should.equal("CreateRepository")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("RepositoryNameExistsException")
ex.response["Error"]["Message"].should.equal(
"Repository named {0} already exists".format("repository_two")
)
@mock_codecommit
def test_create_repository_invalid_repository_name():
client = boto3.client("codecommit", region_name="eu-central-1")
with assert_raises(ClientError) as e:
client.create_repository(repositoryName="in_123_valid_@#$_characters")
ex = e.exception
ex.operation_name.should.equal("CreateRepository")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException")
ex.response["Error"]["Message"].should.equal(
"The repository name is not valid. Repository names can be any valid "
"combination of letters, numbers, "
"periods, underscores, and dashes between 1 and 100 characters in "
"length. Names are case sensitive. "
"For more information, see Limits in the AWS CodeCommit User Guide. "
)
@mock_codecommit
def test_get_repository():
client = boto3.client("codecommit", region_name="eu-central-1")
repository_name = "repository_one"
client.create_repository(
repositoryName=repository_name, repositoryDescription="description repo one"
)
response = client.get_repository(repositoryName=repository_name)
response.should_not.be.none
response.get("repositoryMetadata").should_not.be.none
response.get("repositoryMetadata").get("creationDate").should_not.be.none
response.get("repositoryMetadata").get("lastModifiedDate").should_not.be.none
response.get("repositoryMetadata").get("repositoryId").should_not.be.empty
response.get("repositoryMetadata").get("repositoryName").should.equal(
repository_name
)
response.get("repositoryMetadata").get("repositoryDescription").should.equal(
"description repo one"
)
response.get("repositoryMetadata").get("cloneUrlSsh").should.equal(
"ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
"eu-central-1", "repository_one"
)
)
response.get("repositoryMetadata").get("cloneUrlHttp").should.equal(
"https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
"eu-central-1", "repository_one"
)
)
response.get("repositoryMetadata").get("Arn").should.equal(
"arn:aws:codecommit:{0}:{1}:{2}".format(
"eu-central-1", ACCOUNT_ID, "repository_one"
)
)
response.get("repositoryMetadata").get("accountId").should.equal(ACCOUNT_ID)
client = boto3.client("codecommit", region_name="us-east-1")
with assert_raises(ClientError) as e:
client.get_repository(repositoryName=repository_name)
ex = e.exception
ex.operation_name.should.equal("GetRepository")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("RepositoryDoesNotExistException")
ex.response["Error"]["Message"].should.equal(
"{0} does not exist".format(repository_name)
)
@mock_codecommit
def test_get_repository_invalid_repository_name():
client = boto3.client("codecommit", region_name="eu-central-1")
with assert_raises(ClientError) as e:
client.get_repository(repositoryName="repository_one-@#@")
ex = e.exception
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException")
ex.response["Error"]["Message"].should.equal(
"The repository name is not valid. Repository names can be any valid "
"combination of letters, numbers, "
"periods, underscores, and dashes between 1 and 100 characters in "
"length. Names are case sensitive. "
"For more information, see Limits in the AWS CodeCommit User Guide. "
)
@mock_codecommit
def test_delete_repository():
client = boto3.client("codecommit", region_name="us-east-1")
response = client.create_repository(repositoryName="repository_one")
repository_id_create = response.get("repositoryMetadata").get("repositoryId")
response = client.delete_repository(repositoryName="repository_one")
response.get("repositoryId").should_not.be.none
repository_id_create.should.equal(response.get("repositoryId"))
response = client.delete_repository(repositoryName="unknown_repository")
response.get("repositoryId").should.be.none
@mock_codecommit
def test_delete_repository_invalid_repository_name():
client = boto3.client("codecommit", region_name="us-east-1")
with assert_raises(ClientError) as e:
client.delete_repository(repositoryName="_rep@ository_one")
ex = e.exception
ex.operation_name.should.equal("DeleteRepository")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException")
ex.response["Error"]["Message"].should.equal(
"The repository name is not valid. Repository names can be any valid "
"combination of letters, numbers, "
"periods, underscores, and dashes between 1 and 100 characters in "
"length. Names are case sensitive. "
"For more information, see Limits in the AWS CodeCommit User Guide. "
)

View File

@ -0,0 +1,720 @@
import json
from datetime import datetime
import boto3
import sure # noqa
from botocore.exceptions import ClientError
from nose.tools import assert_raises
from moto import mock_codepipeline, mock_iam
@mock_codepipeline
def test_create_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1")
response = create_basic_codepipeline(client, "test-pipeline")
response["pipeline"].should.equal(
{
"name": "test-pipeline",
"roleArn": "arn:aws:iam::123456789012:role/test-role",
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"runOrder": 1,
"configuration": {
"S3Bucket": "test-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"}],
"inputArtifacts": [],
}
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
"runOrder": 1,
"configuration": {},
"outputArtifacts": [],
"inputArtifacts": [],
}
],
},
],
"version": 1,
}
)
response["tags"].should.equal([{"key": "key", "value": "value"}])
@mock_codepipeline
@mock_iam
def test_create_pipeline_errors():
client = boto3.client("codepipeline", region_name="us-east-1")
client_iam = boto3.client("iam", region_name="us-east-1")
create_basic_codepipeline(client, "test-pipeline")
with assert_raises(ClientError) as e:
create_basic_codepipeline(client, "test-pipeline")
ex = e.exception
ex.operation_name.should.equal("CreatePipeline")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidStructureException")
ex.response["Error"]["Message"].should.equal(
"A pipeline with the name 'test-pipeline' already exists in account '123456789012'"
)
with assert_raises(ClientError) as e:
client.create_pipeline(
pipeline={
"name": "invalid-pipeline",
"roleArn": "arn:aws:iam::123456789012:role/not-existing",
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"runOrder": 1,
},
],
},
],
}
)
ex = e.exception
ex.operation_name.should.equal("CreatePipeline")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidStructureException")
ex.response["Error"]["Message"].should.equal(
"CodePipeline is not authorized to perform AssumeRole on role arn:aws:iam::123456789012:role/not-existing"
)
wrong_role_arn = client_iam.create_role(
RoleName="wrong-role",
AssumeRolePolicyDocument=json.dumps(
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "s3.amazonaws.com"},
"Action": "sts:AssumeRole",
}
],
}
),
)["Role"]["Arn"]
with assert_raises(ClientError) as e:
client.create_pipeline(
pipeline={
"name": "invalid-pipeline",
"roleArn": wrong_role_arn,
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"runOrder": 1,
},
],
},
],
}
)
ex = e.exception
ex.operation_name.should.equal("CreatePipeline")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidStructureException")
ex.response["Error"]["Message"].should.equal(
"CodePipeline is not authorized to perform AssumeRole on role arn:aws:iam::123456789012:role/wrong-role"
)
with assert_raises(ClientError) as e:
client.create_pipeline(
pipeline={
"name": "invalid-pipeline",
"roleArn": get_role_arn(),
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"runOrder": 1,
},
],
},
],
}
)
ex = e.exception
ex.operation_name.should.equal("CreatePipeline")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidStructureException")
ex.response["Error"]["Message"].should.equal(
"Pipeline has only 1 stage(s). There should be a minimum of 2 stages in a pipeline"
)
@mock_codepipeline
def test_get_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1")
create_basic_codepipeline(client, "test-pipeline")
response = client.get_pipeline(name="test-pipeline")
response["pipeline"].should.equal(
{
"name": "test-pipeline",
"roleArn": "arn:aws:iam::123456789012:role/test-role",
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"runOrder": 1,
"configuration": {
"S3Bucket": "test-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"}],
"inputArtifacts": [],
}
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
"runOrder": 1,
"configuration": {},
"outputArtifacts": [],
"inputArtifacts": [],
}
],
},
],
"version": 1,
}
)
response["metadata"]["pipelineArn"].should.equal(
"arn:aws:codepipeline:us-east-1:123456789012:test-pipeline"
)
response["metadata"]["created"].should.be.a(datetime)
response["metadata"]["updated"].should.be.a(datetime)
@mock_codepipeline
def test_get_pipeline_errors():
client = boto3.client("codepipeline", region_name="us-east-1")
with assert_raises(ClientError) as e:
client.get_pipeline(name="not-existing")
ex = e.exception
ex.operation_name.should.equal("GetPipeline")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("PipelineNotFoundException")
ex.response["Error"]["Message"].should.equal(
"Account '123456789012' does not have a pipeline with name 'not-existing'"
)
@mock_codepipeline
def test_update_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1")
create_basic_codepipeline(client, "test-pipeline")
response = client.get_pipeline(name="test-pipeline")
created_time = response["metadata"]["created"]
updated_time = response["metadata"]["updated"]
response = client.update_pipeline(
pipeline={
"name": "test-pipeline",
"roleArn": get_role_arn(),
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"configuration": {
"S3Bucket": "different-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"},],
},
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
},
],
},
],
}
)
response["pipeline"].should.equal(
{
"name": "test-pipeline",
"roleArn": "arn:aws:iam::123456789012:role/test-role",
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"runOrder": 1,
"configuration": {
"S3Bucket": "different-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"}],
"inputArtifacts": [],
}
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
"runOrder": 1,
"configuration": {},
"outputArtifacts": [],
"inputArtifacts": [],
}
],
},
],
"version": 2,
}
)
metadata = client.get_pipeline(name="test-pipeline")["metadata"]
metadata["created"].should.equal(created_time)
metadata["updated"].should.be.greater_than(updated_time)
@mock_codepipeline
def test_update_pipeline_errors():
client = boto3.client("codepipeline", region_name="us-east-1")
with assert_raises(ClientError) as e:
client.update_pipeline(
pipeline={
"name": "not-existing",
"roleArn": get_role_arn(),
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"configuration": {
"S3Bucket": "test-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"},],
},
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
},
],
},
],
}
)
ex = e.exception
ex.operation_name.should.equal("UpdatePipeline")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
ex.response["Error"]["Message"].should.equal(
"The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
)
@mock_codepipeline
def test_list_pipelines():
client = boto3.client("codepipeline", region_name="us-east-1")
name_1 = "test-pipeline-1"
create_basic_codepipeline(client, name_1)
name_2 = "test-pipeline-2"
create_basic_codepipeline(client, name_2)
response = client.list_pipelines()
response["pipelines"].should.have.length_of(2)
response["pipelines"][0]["name"].should.equal(name_1)
response["pipelines"][0]["version"].should.equal(1)
response["pipelines"][0]["created"].should.be.a(datetime)
response["pipelines"][0]["updated"].should.be.a(datetime)
response["pipelines"][1]["name"].should.equal(name_2)
response["pipelines"][1]["version"].should.equal(1)
response["pipelines"][1]["created"].should.be.a(datetime)
response["pipelines"][1]["updated"].should.be.a(datetime)
@mock_codepipeline
def test_delete_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1")
name = "test-pipeline"
create_basic_codepipeline(client, name)
client.list_pipelines()["pipelines"].should.have.length_of(1)
client.delete_pipeline(name=name)
client.list_pipelines()["pipelines"].should.have.length_of(0)
# deleting a not existing pipeline, should raise no exception
client.delete_pipeline(name=name)
@mock_codepipeline
def test_list_tags_for_resource():
client = boto3.client("codepipeline", region_name="us-east-1")
name = "test-pipeline"
create_basic_codepipeline(client, name)
response = client.list_tags_for_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
)
response["tags"].should.equal([{"key": "key", "value": "value"}])
@mock_codepipeline
def test_list_tags_for_resource_errors():
client = boto3.client("codepipeline", region_name="us-east-1")
with assert_raises(ClientError) as e:
client.list_tags_for_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:not-existing"
)
ex = e.exception
ex.operation_name.should.equal("ListTagsForResource")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
ex.response["Error"]["Message"].should.equal(
"The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
)
@mock_codepipeline
def test_tag_resource():
client = boto3.client("codepipeline", region_name="us-east-1")
name = "test-pipeline"
create_basic_codepipeline(client, name)
client.tag_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
tags=[{"key": "key-2", "value": "value-2"}],
)
response = client.list_tags_for_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
)
response["tags"].should.equal(
[{"key": "key", "value": "value"}, {"key": "key-2", "value": "value-2"}]
)
@mock_codepipeline
def test_tag_resource_errors():
client = boto3.client("codepipeline", region_name="us-east-1")
name = "test-pipeline"
create_basic_codepipeline(client, name)
with assert_raises(ClientError) as e:
client.tag_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:not-existing",
tags=[{"key": "key-2", "value": "value-2"}],
)
ex = e.exception
ex.operation_name.should.equal("TagResource")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
ex.response["Error"]["Message"].should.equal(
"The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
)
with assert_raises(ClientError) as e:
client.tag_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
tags=[{"key": "aws:key", "value": "value"}],
)
ex = e.exception
ex.operation_name.should.equal("TagResource")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("InvalidTagsException")
ex.response["Error"]["Message"].should.equal(
"Not allowed to modify system tags. "
"System tags start with 'aws:'. "
"msg=[Caller is an end user and not allowed to mutate system tags]"
)
with assert_raises(ClientError) as e:
client.tag_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
tags=[
{"key": "key-{}".format(i), "value": "value-{}".format(i)}
for i in range(50)
],
)
ex = e.exception
ex.operation_name.should.equal("TagResource")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("TooManyTagsException")
ex.response["Error"]["Message"].should.equal(
"Tag limit exceeded for resource [arn:aws:codepipeline:us-east-1:123456789012:{}].".format(
name
)
)
@mock_codepipeline
def test_untag_resource():
client = boto3.client("codepipeline", region_name="us-east-1")
name = "test-pipeline"
create_basic_codepipeline(client, name)
response = client.list_tags_for_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
)
response["tags"].should.equal([{"key": "key", "value": "value"}])
client.untag_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
tagKeys=["key"],
)
response = client.list_tags_for_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
)
response["tags"].should.have.length_of(0)
# removing a not existing tag should raise no exception
client.untag_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
tagKeys=["key"],
)
@mock_codepipeline
def test_untag_resource_errors():
client = boto3.client("codepipeline", region_name="us-east-1")
with assert_raises(ClientError) as e:
client.untag_resource(
resourceArn="arn:aws:codepipeline:us-east-1:123456789012:not-existing",
tagKeys=["key"],
)
ex = e.exception
ex.operation_name.should.equal("UntagResource")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
ex.response["Error"]["Message"].should.equal(
"The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
)
@mock_iam
def get_role_arn():
client = boto3.client("iam", region_name="us-east-1")
try:
return client.get_role(RoleName="test-role")["Role"]["Arn"]
except ClientError:
return client.create_role(
RoleName="test-role",
AssumeRolePolicyDocument=json.dumps(
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "codepipeline.amazonaws.com"},
"Action": "sts:AssumeRole",
}
],
}
),
)["Role"]["Arn"]
def create_basic_codepipeline(client, name):
return client.create_pipeline(
pipeline={
"name": name,
"roleArn": get_role_arn(),
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"configuration": {
"S3Bucket": "test-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"},],
},
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
},
],
},
],
},
tags=[{"key": "key", "value": "value"}],
)

View File

@ -6,6 +6,7 @@ from nose.tools import assert_raises
from moto import mock_cognitoidentity from moto import mock_cognitoidentity
from moto.cognitoidentity.utils import get_random_identity_id from moto.cognitoidentity.utils import get_random_identity_id
from moto.core import ACCOUNT_ID
@mock_cognitoidentity @mock_cognitoidentity
@ -17,7 +18,9 @@ def test_create_identity_pool():
AllowUnauthenticatedIdentities=False, AllowUnauthenticatedIdentities=False,
SupportedLoginProviders={"graph.facebook.com": "123456789012345"}, SupportedLoginProviders={"graph.facebook.com": "123456789012345"},
DeveloperProviderName="devname", DeveloperProviderName="devname",
OpenIdConnectProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"], OpenIdConnectProviderARNs=[
"arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)
],
CognitoIdentityProviders=[ CognitoIdentityProviders=[
{ {
"ProviderName": "testprovider", "ProviderName": "testprovider",
@ -25,7 +28,7 @@ def test_create_identity_pool():
"ServerSideTokenCheck": True, "ServerSideTokenCheck": True,
} }
], ],
SamlProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"], SamlProviderARNs=["arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)],
) )
assert result["IdentityPoolId"] != "" assert result["IdentityPoolId"] != ""
@ -39,7 +42,9 @@ def test_describe_identity_pool():
AllowUnauthenticatedIdentities=False, AllowUnauthenticatedIdentities=False,
SupportedLoginProviders={"graph.facebook.com": "123456789012345"}, SupportedLoginProviders={"graph.facebook.com": "123456789012345"},
DeveloperProviderName="devname", DeveloperProviderName="devname",
OpenIdConnectProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"], OpenIdConnectProviderARNs=[
"arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)
],
CognitoIdentityProviders=[ CognitoIdentityProviders=[
{ {
"ProviderName": "testprovider", "ProviderName": "testprovider",
@ -47,7 +52,7 @@ def test_describe_identity_pool():
"ServerSideTokenCheck": True, "ServerSideTokenCheck": True,
} }
], ],
SamlProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"], SamlProviderARNs=["arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)],
) )
result = conn.describe_identity_pool(IdentityPoolId=res["IdentityPoolId"]) result = conn.describe_identity_pool(IdentityPoolId=res["IdentityPoolId"])

View File

@ -14,6 +14,7 @@ from jose import jws
from nose.tools import assert_raises from nose.tools import assert_raises
from moto import mock_cognitoidp from moto import mock_cognitoidp
from moto.core import ACCOUNT_ID
@mock_cognitoidp @mock_cognitoidp
@ -132,7 +133,9 @@ def test_create_user_pool_domain_custom_domain_config():
domain = str(uuid.uuid4()) domain = str(uuid.uuid4())
custom_domain_config = { custom_domain_config = {
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/123456789012" "CertificateArn": "arn:aws:acm:us-east-1:{}:certificate/123456789012".format(
ACCOUNT_ID
)
} }
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
result = conn.create_user_pool_domain( result = conn.create_user_pool_domain(
@ -177,7 +180,9 @@ def test_update_user_pool_domain():
domain = str(uuid.uuid4()) domain = str(uuid.uuid4())
custom_domain_config = { custom_domain_config = {
"CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/123456789012" "CertificateArn": "arn:aws:acm:us-east-1:{}:certificate/123456789012".format(
ACCOUNT_ID
)
} }
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
conn.create_user_pool_domain(UserPoolId=user_pool_id, Domain=domain) conn.create_user_pool_domain(UserPoolId=user_pool_id, Domain=domain)

View File

@ -7,6 +7,7 @@ from nose.tools import assert_raises
from moto import mock_s3 from moto import mock_s3
from moto.config import mock_config from moto.config import mock_config
from moto.core import ACCOUNT_ID
@mock_config @mock_config
@ -397,7 +398,9 @@ def test_put_configuration_aggregator():
account_aggregation_source account_aggregation_source
] ]
assert ( assert (
"arn:aws:config:us-west-2:123456789012:config-aggregator/config-aggregator-" "arn:aws:config:us-west-2:{}:config-aggregator/config-aggregator-".format(
ACCOUNT_ID
)
in result["ConfigurationAggregator"]["ConfigurationAggregatorArn"] in result["ConfigurationAggregator"]["ConfigurationAggregatorArn"]
) )
assert ( assert (
@ -626,10 +629,10 @@ def test_put_aggregation_authorization():
Tags=[{"Key": "tag", "Value": "a"}], Tags=[{"Key": "tag", "Value": "a"}],
) )
assert ( assert result["AggregationAuthorization"][
result["AggregationAuthorization"]["AggregationAuthorizationArn"] "AggregationAuthorizationArn"
== "arn:aws:config:us-west-2:123456789012:" ] == "arn:aws:config:us-west-2:{}:aggregation-authorization/012345678910/us-east-1".format(
"aggregation-authorization/012345678910/us-east-1" ACCOUNT_ID
) )
assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910" assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910"
assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1" assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1"
@ -641,10 +644,10 @@ def test_put_aggregation_authorization():
result = client.put_aggregation_authorization( result = client.put_aggregation_authorization(
AuthorizedAccountId="012345678910", AuthorizedAwsRegion="us-east-1" AuthorizedAccountId="012345678910", AuthorizedAwsRegion="us-east-1"
) )
assert ( assert result["AggregationAuthorization"][
result["AggregationAuthorization"]["AggregationAuthorizationArn"] "AggregationAuthorizationArn"
== "arn:aws:config:us-west-2:123456789012:" ] == "arn:aws:config:us-west-2:{}:aggregation-authorization/012345678910/us-east-1".format(
"aggregation-authorization/012345678910/us-east-1" ACCOUNT_ID
) )
assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910" assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910"
assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1" assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1"
@ -1416,7 +1419,7 @@ def test_list_aggregate_discovered_resource():
assert len(result["ResourceIdentifiers"]) == 12 assert len(result["ResourceIdentifiers"]) == 12
for x in range(0, 10): for x in range(0, 10):
assert result["ResourceIdentifiers"][x] == { assert result["ResourceIdentifiers"][x] == {
"SourceAccountId": "123456789012", "SourceAccountId": ACCOUNT_ID,
"ResourceType": "AWS::S3::Bucket", "ResourceType": "AWS::S3::Bucket",
"ResourceId": "bucket{}".format(x), "ResourceId": "bucket{}".format(x),
"ResourceName": "bucket{}".format(x), "ResourceName": "bucket{}".format(x),
@ -1424,7 +1427,7 @@ def test_list_aggregate_discovered_resource():
} }
for x in range(11, 12): for x in range(11, 12):
assert result["ResourceIdentifiers"][x] == { assert result["ResourceIdentifiers"][x] == {
"SourceAccountId": "123456789012", "SourceAccountId": ACCOUNT_ID,
"ResourceType": "AWS::S3::Bucket", "ResourceType": "AWS::S3::Bucket",
"ResourceId": "eu-bucket{}".format(x), "ResourceId": "eu-bucket{}".format(x),
"ResourceName": "eu-bucket{}".format(x), "ResourceName": "eu-bucket{}".format(x),

View File

@ -10,7 +10,7 @@ from nose.tools import assert_raises
from moto import mock_iam, mock_ec2, mock_s3, mock_sts, mock_elbv2, mock_rds2 from moto import mock_iam, mock_ec2, mock_s3, mock_sts, mock_elbv2, mock_rds2
from moto.core import set_initial_no_auth_action_count from moto.core import set_initial_no_auth_action_count
from moto.iam.models import ACCOUNT_ID from moto.core import ACCOUNT_ID
from uuid import uuid4 from uuid import uuid4

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