commit
74ad2d7d3c
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,8 @@ from .batch import mock_batch # noqa
|
||||
from .cloudformation import mock_cloudformation # noqa
|
||||
from .cloudformation import mock_cloudformation_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_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 .dynamodbstreams import mock_dynamodbstreams # 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 .ecs import mock_ecs, mock_ecs_deprecated # noqa
|
||||
from .elb import mock_elb, mock_elb_deprecated # noqa
|
||||
|
@ -13,8 +13,9 @@ import cryptography.hazmat.primitives.asymmetric.rsa
|
||||
from cryptography.hazmat.primitives import serialization, hashes
|
||||
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-----
|
||||
MIIEKDCCAxCgAwIBAgIQAQAhJYiw+lmnd+8Fe2Yn3zANBgkqhkiG9w0BAQsFADBC
|
||||
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
|
||||
|
@ -101,3 +101,12 @@ class ApiKeyNotFoundException(RESTError):
|
||||
super(ApiKeyNotFoundException, self).__init__(
|
||||
"NotFoundException", "Invalid API Key identifier specified"
|
||||
)
|
||||
|
||||
|
||||
class ApiKeyAlreadyExists(RESTError):
|
||||
code = 409
|
||||
|
||||
def __init__(self):
|
||||
super(ApiKeyAlreadyExists, self).__init__(
|
||||
"ConflictException", "API Key already exists"
|
||||
)
|
||||
|
@ -32,6 +32,7 @@ from .exceptions import (
|
||||
RoleNotSpecified,
|
||||
NoIntegrationDefined,
|
||||
NoMethodDefined,
|
||||
ApiKeyAlreadyExists,
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
self.keys[key["id"]] = key
|
||||
return key
|
||||
@ -842,3 +847,11 @@ class APIGatewayBackend(BaseBackend):
|
||||
apigateway_backends = {}
|
||||
for region_name in Session().get_available_regions("apigateway"):
|
||||
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)
|
||||
|
@ -9,6 +9,7 @@ from .exceptions import (
|
||||
BadRequestException,
|
||||
CrossAccountNotAllowed,
|
||||
StageNotFoundException,
|
||||
ApiKeyAlreadyExists,
|
||||
)
|
||||
|
||||
|
||||
@ -302,7 +303,17 @@ class APIGatewayResponse(BaseResponse):
|
||||
self.setup_class(request, full_url, headers)
|
||||
|
||||
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":
|
||||
apikeys_response = self.backend.get_apikeys()
|
||||
return 200, {}, json.dumps({"item": apikeys_response})
|
||||
|
@ -1,10 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
import time
|
||||
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
ACCOUNT_ID = 123456789012
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
class TaggableResourceMixin(object):
|
||||
@ -77,5 +78,9 @@ class AthenaBackend(BaseBackend):
|
||||
|
||||
|
||||
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)
|
||||
|
@ -23,7 +23,8 @@ import traceback
|
||||
import weakref
|
||||
import requests.adapters
|
||||
|
||||
import boto.awslambda
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.exceptions import RESTError
|
||||
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.dynamodb2 import dynamodb_backends2
|
||||
from moto.dynamodbstreams import dynamodbstreams_backends
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ACCOUNT_ID = "123456789012"
|
||||
|
||||
|
||||
try:
|
||||
from tempfile import TemporaryDirectory
|
||||
@ -1044,10 +1044,10 @@ def do_validate_s3():
|
||||
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 = {
|
||||
_region.name: LambdaBackend(_region.name) for _region in boto.awslambda.regions()
|
||||
}
|
||||
|
||||
lambda_backends["ap-southeast-2"] = LambdaBackend("ap-southeast-2")
|
||||
lambda_backends["us-gov-west-1"] = LambdaBackend("us-gov-west-1")
|
||||
lambda_backends = {}
|
||||
for region in Session().get_available_regions("lambda"):
|
||||
lambda_backends[region] = LambdaBackend(region)
|
||||
for region in Session().get_available_regions("lambda", partition_name="aws-us-gov"):
|
||||
lambda_backends[region] = LambdaBackend(region)
|
||||
for region in Session().get_available_regions("lambda", partition_name="aws-cn"):
|
||||
lambda_backends[region] = LambdaBackend(region)
|
||||
|
@ -8,6 +8,8 @@ from moto.awslambda import lambda_backends
|
||||
from moto.batch import batch_backends
|
||||
from moto.cloudformation import cloudformation_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.cognitoidp import cognitoidp_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.dynamodbstreams import dynamodbstreams_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.ecs import ecs_backends
|
||||
from moto.elb import elb_backends
|
||||
@ -60,6 +63,8 @@ BACKENDS = {
|
||||
"batch": batch_backends,
|
||||
"cloudformation": cloudformation_backends,
|
||||
"cloudwatch": cloudwatch_backends,
|
||||
"codecommit": codecommit_backends,
|
||||
"codepipeline": codepipeline_backends,
|
||||
"cognito-identity": cognitoidentity_backends,
|
||||
"cognito-idp": cognitoidp_backends,
|
||||
"config": config_backends,
|
||||
@ -69,6 +74,7 @@ BACKENDS = {
|
||||
"dynamodb2": dynamodb_backends2,
|
||||
"dynamodbstreams": dynamodbstreams_backends,
|
||||
"ec2": ec2_backends,
|
||||
"ec2_instance_connect": ec2_instance_connect_backends,
|
||||
"ecr": ecr_backends,
|
||||
"ecs": ecs_backends,
|
||||
"elb": elb_backends,
|
||||
|
@ -1,5 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
import boto3
|
||||
import re
|
||||
import requests.adapters
|
||||
from itertools import cycle
|
||||
@ -12,6 +11,8 @@ import docker
|
||||
import functools
|
||||
import threading
|
||||
import dateutil.parser
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.iam import iam_backends
|
||||
from moto.ec2 import ec2_backends
|
||||
@ -28,11 +29,10 @@ from .utils import (
|
||||
from moto.ec2.exceptions import InvalidSubnetIdError
|
||||
from moto.ec2.models import INSTANCE_TYPES as EC2_INSTANCE_TYPES
|
||||
from moto.iam.exceptions import IAMNotFoundException
|
||||
|
||||
from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
|
||||
|
||||
_orig_adapter_send = requests.adapters.HTTPAdapter.send
|
||||
logger = logging.getLogger(__name__)
|
||||
DEFAULT_ACCOUNT_ID = 123456789012
|
||||
COMPUTE_ENVIRONMENT_NAME_REGEX = re.compile(
|
||||
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.container_properties = container_properties
|
||||
self.arn = None
|
||||
self.status = "INACTIVE"
|
||||
self.status = "ACTIVE"
|
||||
|
||||
if parameters is None:
|
||||
parameters = {}
|
||||
@ -285,7 +285,7 @@ class JobDefinition(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
|
||||
|
||||
@ -301,6 +301,7 @@ class Job(threading.Thread, BaseModel):
|
||||
self.job_name = name
|
||||
self.job_id = str(uuid.uuid4())
|
||||
self.job_definition = job_def
|
||||
self.container_overrides = container_overrides
|
||||
self.job_queue = job_queue
|
||||
self.job_state = "SUBMITTED" # One of SUBMITTED | PENDING | RUNNABLE | STARTING | RUNNING | SUCCEEDED | FAILED
|
||||
self.job_queue.jobs.append(self)
|
||||
@ -357,6 +358,11 @@ class Job(threading.Thread, BaseModel):
|
||||
result["statusReason"] = self.job_stopped_reason
|
||||
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):
|
||||
"""
|
||||
Run the container.
|
||||
@ -375,8 +381,33 @@ class Job(threading.Thread, BaseModel):
|
||||
self.job_state = "PENDING"
|
||||
time.sleep(1)
|
||||
|
||||
image = "alpine:latest"
|
||||
cmd = '/bin/sh -c "for a in `seq 1 10`; do echo Hello World; sleep 1; done"'
|
||||
image = self.job_definition.container_properties.get(
|
||||
"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)
|
||||
|
||||
self.job_state = "RUNNABLE"
|
||||
@ -384,8 +415,16 @@ class Job(threading.Thread, BaseModel):
|
||||
time.sleep(1)
|
||||
|
||||
self.job_state = "STARTING"
|
||||
log_config = docker.types.LogConfig(type=docker.types.LogConfig.types.JSON)
|
||||
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_started_at = datetime.datetime.now()
|
||||
@ -815,8 +854,10 @@ class BatchBackend(BaseBackend):
|
||||
raise InvalidParameterValueException(
|
||||
"computeResources must contain {0}".format(param)
|
||||
)
|
||||
|
||||
if self.iam_backend.get_role_by_arn(cr["instanceRole"]) is None:
|
||||
for profile in self.iam_backend.get_instance_profiles():
|
||||
if profile.arn == cr["instanceRole"]:
|
||||
break
|
||||
else:
|
||||
raise InvalidParameterValueException(
|
||||
"could not find instanceRole {0}".format(cr["instanceRole"])
|
||||
)
|
||||
@ -1208,7 +1249,13 @@ class BatchBackend(BaseBackend):
|
||||
if queue is None:
|
||||
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
|
||||
|
||||
# Here comes the fun
|
||||
@ -1271,7 +1318,10 @@ class BatchBackend(BaseBackend):
|
||||
job.terminate(reason)
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("batch")
|
||||
batch_backends = {
|
||||
region: BatchBackend(region_name=region) for region in available_regions
|
||||
}
|
||||
batch_backends = {}
|
||||
for region in Session().get_available_regions("batch"):
|
||||
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)
|
||||
|
@ -4,7 +4,8 @@ import json
|
||||
import yaml
|
||||
import uuid
|
||||
|
||||
import boto.cloudformation
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
@ -717,5 +718,13 @@ class CloudFormationBackend(BaseBackend):
|
||||
|
||||
|
||||
cloudformation_backends = {}
|
||||
for region in boto.cloudformation.regions():
|
||||
cloudformation_backends[region.name] = CloudFormationBackend()
|
||||
for region in Session().get_available_regions("cloudformation"):
|
||||
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()
|
||||
|
@ -1,5 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
import collections
|
||||
import functools
|
||||
import logging
|
||||
import copy
|
||||
@ -11,6 +10,7 @@ from moto.awslambda import models as lambda_models
|
||||
from moto.batch import models as batch_models
|
||||
from moto.cloudwatch import models as cloudwatch_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.dynamodb2 import models as dynamodb2_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.sns import models as sns_models
|
||||
from moto.sqs import models as sqs_models
|
||||
from moto.core import ACCOUNT_ID
|
||||
from .utils import random_suffix
|
||||
from .exceptions import (
|
||||
ExportNotFound,
|
||||
@ -404,7 +405,7 @@ def parse_output(output_logical_id, output_json, resources_map):
|
||||
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
|
||||
without needing to create a full dependency tree. Upon creation, each
|
||||
@ -431,7 +432,7 @@ class ResourceMap(collections.Mapping):
|
||||
|
||||
# Create the default resources
|
||||
self._parsed_resources = {
|
||||
"AWS::AccountId": "123456789012",
|
||||
"AWS::AccountId": ACCOUNT_ID,
|
||||
"AWS::Region": self._region_name,
|
||||
"AWS::StackId": stack_id,
|
||||
"AWS::StackName": stack_name,
|
||||
@ -633,7 +634,7 @@ class ResourceMap(collections.Mapping):
|
||||
raise last_exception
|
||||
|
||||
|
||||
class OutputMap(collections.Mapping):
|
||||
class OutputMap(collections_abc.Mapping):
|
||||
def __init__(self, resources, template, stack_id):
|
||||
self._template = template
|
||||
self._stack_id = stack_id
|
||||
|
@ -7,6 +7,7 @@ from six.moves.urllib.parse import urlparse
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.core.utils import amzn_request_id
|
||||
from moto.s3 import s3_backend
|
||||
from moto.core import ACCOUNT_ID
|
||||
from .models import cloudformation_backends
|
||||
from .exceptions import ValidationError
|
||||
|
||||
@ -425,7 +426,9 @@ class CloudFormationResponse(BaseResponse):
|
||||
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
|
||||
|
||||
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:
|
||||
stackset.execution_role = "AWSCloudFormationStackSetExecutionRole"
|
||||
|
||||
@ -1051,11 +1054,14 @@ STOP_STACK_SET_OPERATION_RESPONSE_TEMPLATE = """<StopStackSetOperationResponse x
|
||||
</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>
|
||||
<StackSetOperation>
|
||||
<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>
|
||||
<CreationTimestamp>{{ operation.CreationTimestamp }}</CreationTimestamp>
|
||||
<OperationId>{{ operation.OperationId }}</OperationId>
|
||||
@ -1072,15 +1078,19 @@ DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = """<DescribeStackSetOperationRes
|
||||
</ResponseMetadata>
|
||||
</DescribeStackSetOperationResponse>
|
||||
"""
|
||||
)
|
||||
|
||||
LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = """<ListStackSetOperationResultsResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
|
||||
LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = (
|
||||
"""<ListStackSetOperationResultsResponse xmlns="http://internal.amazon.com/coral/com.amazonaws.maestro.service.v20160713/">
|
||||
<ListStackSetOperationResultsResult>
|
||||
<Summaries>
|
||||
{% for instance in operation.Instances %}
|
||||
{% for account, region in instance.items() %}
|
||||
<member>
|
||||
<AccountGateResult>
|
||||
<StatusReason>Function not found: arn:aws:lambda:us-west-2:123456789012:function:AWSCloudFormationStackSetAccountGate</StatusReason>
|
||||
<StatusReason>Function not found: arn:aws:lambda:us-west-2:"""
|
||||
+ ACCOUNT_ID
|
||||
+ """:function:AWSCloudFormationStackSetAccountGate</StatusReason>
|
||||
<Status>SKIPPED</Status>
|
||||
</AccountGateResult>
|
||||
<Region>{{ region }}</Region>
|
||||
@ -1096,3 +1106,4 @@ LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = """<ListStackSetOperationRe
|
||||
</ResponseMetadata>
|
||||
</ListStackSetOperationResultsResponse>
|
||||
"""
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ import os
|
||||
import string
|
||||
|
||||
from cfnlint import decode, core
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
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):
|
||||
return "arn:aws:cloudformation:{}:123456789012:stackset/{}".format(
|
||||
region_name, stackset_id
|
||||
return "arn:aws:cloudformation:{}:{}:stackset/{}".format(
|
||||
region_name, ACCOUNT_ID, stackset_id
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
import json
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.utils import iso_8601_datetime_with_milliseconds
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.exceptions import RESTError
|
||||
import boto.ec2.cloudwatch
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.tz import tzutc
|
||||
from uuid import uuid4
|
||||
from .utils import make_arn_for_dashboard
|
||||
|
||||
DEFAULT_ACCOUNT_ID = 123456789012
|
||||
from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
|
||||
|
||||
_EMPTY_LIST = tuple()
|
||||
|
||||
@ -431,5 +433,11 @@ class LogGroup(BaseModel):
|
||||
|
||||
|
||||
cloudwatch_backends = {}
|
||||
for region in boto.ec2.cloudwatch.regions():
|
||||
cloudwatch_backends[region.name] = CloudWatchBackend()
|
||||
for region in Session().get_available_regions("cloudwatch"):
|
||||
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()
|
||||
|
4
moto/codecommit/__init__.py
Normal file
4
moto/codecommit/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .models import codecommit_backends
|
||||
from ..core.models import base_decorator
|
||||
|
||||
mock_codecommit = base_decorator(codecommit_backends)
|
35
moto/codecommit/exceptions.py
Normal file
35
moto/codecommit/exceptions.py
Normal 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
69
moto/codecommit/models.py
Normal 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()
|
57
moto/codecommit/responses.py
Normal file
57
moto/codecommit/responses.py
Normal 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
6
moto/codecommit/urls.py
Normal 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}
|
4
moto/codepipeline/__init__.py
Normal file
4
moto/codepipeline/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .models import codepipeline_backends
|
||||
from ..core.models import base_decorator
|
||||
|
||||
mock_codepipeline = base_decorator(codepipeline_backends)
|
44
moto/codepipeline/exceptions.py
Normal file
44
moto/codepipeline/exceptions.py
Normal 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
218
moto/codepipeline/models.py
Normal 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()
|
62
moto/codepipeline/responses.py
Normal file
62
moto/codepipeline/responses.py
Normal 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 ""
|
6
moto/codepipeline/urls.py
Normal file
6
moto/codepipeline/urls.py
Normal 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}
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import boto.cognito.identity
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
@ -136,5 +136,13 @@ class CognitoIdentityBackend(BaseBackend):
|
||||
|
||||
|
||||
cognitoidentity_backends = {}
|
||||
for region in boto.cognito.identity.regions():
|
||||
cognitoidentity_backends[region.name] = CognitoIdentityBackend(region.name)
|
||||
for region in Session().get_available_regions("cognito-identity"):
|
||||
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)
|
||||
|
@ -9,7 +9,7 @@ import os
|
||||
import time
|
||||
import uuid
|
||||
|
||||
import boto.cognito.identity
|
||||
from boto3 import Session
|
||||
from jose import jws
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
@ -749,8 +749,14 @@ class CognitoIdpBackend(BaseBackend):
|
||||
|
||||
|
||||
cognitoidp_backends = {}
|
||||
for region in boto.cognito.identity.regions():
|
||||
cognitoidp_backends[region.name] = CognitoIdpBackend(region.name)
|
||||
for region in Session().get_available_regions("cognito-idp"):
|
||||
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
|
||||
|
@ -3,3 +3,8 @@ try:
|
||||
except ImportError:
|
||||
# python 2.6 or earlier, use backport
|
||||
from ordereddict import OrderedDict # noqa
|
||||
|
||||
try:
|
||||
import collections.abc as collections_abc # noqa
|
||||
except ImportError:
|
||||
import collections as collections_abc # noqa
|
||||
|
@ -45,7 +45,8 @@ from moto.config.exceptions import (
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.s3.config import s3_config_query
|
||||
|
||||
DEFAULT_ACCOUNT_ID = "123456789012"
|
||||
from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
|
||||
|
||||
POP_STRINGS = [
|
||||
"capitalizeStart",
|
||||
"CapitalizeStart",
|
||||
@ -1083,6 +1084,9 @@ class ConfigBackend(BaseBackend):
|
||||
|
||||
|
||||
config_backends = {}
|
||||
boto3_session = Session()
|
||||
for region in boto3_session.get_available_regions("config"):
|
||||
for region in 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()
|
||||
|
@ -1,6 +1,6 @@
|
||||
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
|
||||
|
||||
moto_api_backends = {"global": moto_api_backend}
|
||||
|
@ -24,7 +24,8 @@ from botocore.awsrequest import AWSRequest
|
||||
from botocore.credentials import Credentials
|
||||
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.core.exceptions import (
|
||||
SignatureDoesNotMatchError,
|
||||
|
@ -23,6 +23,9 @@ from .utils import (
|
||||
)
|
||||
|
||||
|
||||
ACCOUNT_ID = os.environ.get("MOTO_ACCOUNT_ID", "123456789012")
|
||||
|
||||
|
||||
class BaseMockAWS(object):
|
||||
nested_count = 0
|
||||
|
||||
|
@ -304,3 +304,27 @@ def path_url(url):
|
||||
if parsed_url.query:
|
||||
path = path + "?" + parsed_url.query
|
||||
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
|
||||
|
@ -1,7 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
import boto.datapipeline
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys
|
||||
@ -142,5 +143,11 @@ class DataPipelineBackend(BaseBackend):
|
||||
|
||||
|
||||
datapipeline_backends = {}
|
||||
for region in boto.datapipeline.regions():
|
||||
datapipeline_backends[region.name] = DataPipelineBackend()
|
||||
for region in Session().get_available_regions("datapipeline"):
|
||||
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)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import collections
|
||||
import six
|
||||
from moto.compat import collections_abc
|
||||
from moto.core.utils import get_random_hex
|
||||
|
||||
|
||||
@ -8,13 +8,13 @@ def get_random_pipeline_id():
|
||||
|
||||
|
||||
def remove_capitalization_of_dict_keys(obj):
|
||||
if isinstance(obj, collections.Mapping):
|
||||
if isinstance(obj, collections_abc.Mapping):
|
||||
result = obj.__class__()
|
||||
for key, value in obj.items():
|
||||
normalized_key = key[:1].lower() + key[1:]
|
||||
result[normalized_key] = remove_capitalization_of_dict_keys(value)
|
||||
return result
|
||||
elif isinstance(obj, collections.Iterable) and not isinstance(
|
||||
elif isinstance(obj, collections_abc.Iterable) and not isinstance(
|
||||
obj, six.string_types
|
||||
):
|
||||
result = obj.__class__()
|
||||
|
@ -1,4 +1,5 @@
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
@ -226,5 +227,9 @@ class DataSyncBackend(BaseBackend):
|
||||
|
||||
|
||||
datasync_backends = {}
|
||||
for region in boto3.Session().get_available_regions("datasync"):
|
||||
datasync_backends[region] = DataSyncBackend(region_name=region)
|
||||
for region in Session().get_available_regions("datasync"):
|
||||
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)
|
||||
|
@ -6,6 +6,7 @@ import json
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.utils import unix_time
|
||||
from moto.core import ACCOUNT_ID
|
||||
from .comparisons import get_comparison_func
|
||||
|
||||
|
||||
@ -277,8 +278,8 @@ class Table(BaseModel):
|
||||
if attribute_name == "StreamArn":
|
||||
region = "us-east-1"
|
||||
time = "2000-01-01T00:00:00.000"
|
||||
return "arn:aws:dynamodb:{0}:123456789012:table/{1}/stream/{2}".format(
|
||||
region, self.name, time
|
||||
return "arn:aws:dynamodb:{0}:{1}:table/{2}/stream/{3}".format(
|
||||
region, ACCOUNT_ID, self.name, time
|
||||
)
|
||||
raise UnformattedGetAttTemplateException()
|
||||
|
||||
|
@ -8,7 +8,7 @@ import re
|
||||
import uuid
|
||||
import six
|
||||
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
from botocore.exceptions import ParamValidationError
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
@ -586,7 +586,9 @@ class StreamRecord(BaseModel):
|
||||
self.record["dynamodb"]["OldImage"] = old_a
|
||||
|
||||
# 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):
|
||||
return self.record
|
||||
@ -1484,7 +1486,10 @@ class DynamoDBBackend(BaseBackend):
|
||||
return table.ttl
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("dynamodb")
|
||||
dynamodb_backends = {
|
||||
region: DynamoDBBackend(region_name=region) for region in available_regions
|
||||
}
|
||||
dynamodb_backends = {}
|
||||
for region in Session().get_available_regions("dynamodb"):
|
||||
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)
|
||||
|
@ -438,9 +438,12 @@ class DynamoHandler(BaseResponse):
|
||||
all_indexes = (table.global_indexes or []) + (table.indexes or [])
|
||||
indexes_by_name = dict((i["IndexName"], i) for i in all_indexes)
|
||||
if index_name not in indexes_by_name:
|
||||
raise ValueError(
|
||||
"Invalid index: %s for table: %s. Available indexes are: %s"
|
||||
% (index_name, name, ", ".join(indexes_by_name.keys()))
|
||||
er = "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException"
|
||||
return self.error(
|
||||
er,
|
||||
"Invalid index: {} for table: {}. Available indexes are: {}".format(
|
||||
index_name, name, ", ".join(indexes_by_name.keys())
|
||||
),
|
||||
)
|
||||
|
||||
index = indexes_by_name[index_name]["KeySchema"]
|
||||
@ -481,7 +484,9 @@ class DynamoHandler(BaseResponse):
|
||||
]
|
||||
elif "begins_with" in range_key_expression:
|
||||
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:
|
||||
range_values = [value_alias_map[range_key_expression_components[2]]]
|
||||
else:
|
||||
|
@ -2,9 +2,10 @@ from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import json
|
||||
import boto3
|
||||
import base64
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.dynamodb2.models import dynamodb_backends
|
||||
|
||||
@ -139,7 +140,14 @@ class DynamoDBStreamsBackend(BaseBackend):
|
||||
return json.dumps(shard_iterator.get(limit))
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("dynamodbstreams")
|
||||
dynamodbstreams_backends = {
|
||||
region: DynamoDBStreamsBackend(region=region) for region in available_regions
|
||||
}
|
||||
dynamodbstreams_backends = {}
|
||||
for region in Session().get_available_regions("dynamodbstreams"):
|
||||
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)
|
||||
|
@ -8,9 +8,9 @@ import os
|
||||
import re
|
||||
import six
|
||||
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
|
||||
import weakref
|
||||
@ -1473,7 +1473,13 @@ class Zone(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 = {
|
||||
"ap-south-1": [
|
||||
@ -1536,6 +1542,11 @@ class RegionsAndZonesBackend(object):
|
||||
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": [
|
||||
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"),
|
||||
@ -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-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": [
|
||||
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"),
|
||||
],
|
||||
"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": [
|
||||
Zone(
|
||||
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"
|
||||
),
|
||||
],
|
||||
"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=[]):
|
||||
@ -2449,6 +2493,7 @@ class VPC(TaggedEC2Resource):
|
||||
self.is_default = "true" if is_default else "false"
|
||||
self.enable_dns_support = "true"
|
||||
self.classic_link_enabled = "false"
|
||||
self.classic_link_dns_supported = "false"
|
||||
# This attribute is set to 'true' only for default VPCs
|
||||
# or VPCs created using the wizard of the VPC console
|
||||
self.enable_dns_hostnames = "true" if is_default else "false"
|
||||
@ -3306,6 +3351,7 @@ class Route(object):
|
||||
local=False,
|
||||
gateway=None,
|
||||
instance=None,
|
||||
nat_gateway=None,
|
||||
interface=None,
|
||||
vpc_pcx=None,
|
||||
):
|
||||
@ -3315,6 +3361,7 @@ class Route(object):
|
||||
self.local = local
|
||||
self.gateway = gateway
|
||||
self.instance = instance
|
||||
self.nat_gateway = nat_gateway
|
||||
self.interface = interface
|
||||
self.vpc_pcx = vpc_pcx
|
||||
|
||||
@ -3327,6 +3374,7 @@ class Route(object):
|
||||
gateway_id = properties.get("GatewayId")
|
||||
instance_id = properties.get("InstanceId")
|
||||
interface_id = properties.get("NetworkInterfaceId")
|
||||
nat_gateway_id = properties.get("NatGatewayId")
|
||||
pcx_id = properties.get("VpcPeeringConnectionId")
|
||||
|
||||
route_table_id = properties["RouteTableId"]
|
||||
@ -3336,6 +3384,7 @@ class Route(object):
|
||||
destination_cidr_block=properties.get("DestinationCidrBlock"),
|
||||
gateway_id=gateway_id,
|
||||
instance_id=instance_id,
|
||||
nat_gateway_id=nat_gateway_id,
|
||||
interface_id=interface_id,
|
||||
vpc_peering_connection_id=pcx_id,
|
||||
)
|
||||
@ -3353,6 +3402,7 @@ class RouteBackend(object):
|
||||
local=False,
|
||||
gateway_id=None,
|
||||
instance_id=None,
|
||||
nat_gateway_id=None,
|
||||
interface_id=None,
|
||||
vpc_peering_connection_id=None,
|
||||
):
|
||||
@ -3373,12 +3423,17 @@ class RouteBackend(object):
|
||||
except ValueError:
|
||||
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_table,
|
||||
destination_cidr_block,
|
||||
local=local,
|
||||
gateway=gateway,
|
||||
instance=self.get_instance(instance_id) if instance_id else None,
|
||||
nat_gateway=nat_gateway,
|
||||
interface=None,
|
||||
vpc_pcx=self.get_vpc_peering_connection(vpc_peering_connection_id)
|
||||
if vpc_peering_connection_id
|
||||
@ -4827,7 +4882,35 @@ class NatGatewayBackend(object):
|
||||
super(NatGatewayBackend, self).__init__()
|
||||
|
||||
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):
|
||||
nat_gateway = NatGateway(self, subnet_id, allocation_id)
|
||||
|
@ -6,6 +6,7 @@ from moto.core.responses import BaseResponse
|
||||
from moto.core.utils import camelcase_to_underscores
|
||||
from moto.ec2.utils import filters_from_querystring, dict_from_querystring
|
||||
from moto.elbv2 import elbv2_backends
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
class InstanceResponse(BaseResponse):
|
||||
@ -246,10 +247,13 @@ class InstanceResponse(BaseResponse):
|
||||
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>
|
||||
<reservationId>{{ reservation.id }}</reservationId>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<groupSet>
|
||||
<item>
|
||||
<groupId>sg-245f6a01</groupId>
|
||||
@ -331,7 +335,9 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
||||
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
|
||||
{% endif %}
|
||||
<description>Primary network interface</description>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<status>in-use</status>
|
||||
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
|
||||
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||
@ -354,7 +360,9 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>123456789012</ipOwnerId>
|
||||
<ipOwnerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
<privateIpAddressesSet>
|
||||
@ -364,7 +372,9 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>123456789012</ipOwnerId>
|
||||
<ipOwnerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
</item>
|
||||
@ -376,14 +386,18 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
||||
{% endfor %}
|
||||
</instancesSet>
|
||||
</RunInstancesResponse>"""
|
||||
)
|
||||
|
||||
EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||
EC2_DESCRIBE_INSTANCES = (
|
||||
"""<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||
<requestId>fdcdcab1-ae5c-489e-9c33-4637c5dda355</requestId>
|
||||
<reservationSet>
|
||||
{% for reservation in reservations %}
|
||||
<item>
|
||||
<reservationId>{{ reservation.id }}</reservationId>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<groupSet>
|
||||
{% for group in reservation.dynamic_group_list %}
|
||||
<item>
|
||||
@ -476,7 +490,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
|
||||
{% endfor %}
|
||||
</blockDeviceMapping>
|
||||
<virtualizationType>{{ instance.virtualization_type }}</virtualizationType>
|
||||
<clientToken>ABCDE1234567890123</clientToken>
|
||||
<clientToken>ABCDE"""
|
||||
+ ACCOUNT_ID
|
||||
+ """3</clientToken>
|
||||
{% if instance.get_tags() %}
|
||||
<tagSet>
|
||||
{% for tag in instance.get_tags() %}
|
||||
@ -499,7 +515,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
|
||||
<vpcId>{{ nic.subnet.vpc_id }}</vpcId>
|
||||
{% endif %}
|
||||
<description>Primary network interface</description>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<status>in-use</status>
|
||||
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
|
||||
<privateIpAddress>{{ nic.private_ip_address }}</privateIpAddress>
|
||||
@ -526,7 +544,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>123456789012</ipOwnerId>
|
||||
<ipOwnerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
<privateIpAddressesSet>
|
||||
@ -536,7 +556,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
|
||||
{% if nic.public_ip %}
|
||||
<association>
|
||||
<publicIp>{{ nic.public_ip }}</publicIp>
|
||||
<ipOwnerId>123456789012</ipOwnerId>
|
||||
<ipOwnerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ipOwnerId>
|
||||
</association>
|
||||
{% endif %}
|
||||
</item>
|
||||
@ -554,6 +576,7 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns="http://ec2.amazona
|
||||
<nextToken>{{ next_token }}</nextToken>
|
||||
{% endif %}
|
||||
</DescribeInstancesResponse>"""
|
||||
)
|
||||
|
||||
EC2_TERMINATE_INSTANCES = """
|
||||
<TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||
|
@ -18,6 +18,7 @@ class RouteTables(BaseResponse):
|
||||
destination_cidr_block = self._get_param("DestinationCidrBlock")
|
||||
gateway_id = self._get_param("GatewayId")
|
||||
instance_id = self._get_param("InstanceId")
|
||||
nat_gateway_id = self._get_param("NatGatewayId")
|
||||
interface_id = self._get_param("NetworkInterfaceId")
|
||||
pcx_id = self._get_param("VpcPeeringConnectionId")
|
||||
|
||||
@ -26,6 +27,7 @@ class RouteTables(BaseResponse):
|
||||
destination_cidr_block,
|
||||
gateway_id=gateway_id,
|
||||
instance_id=instance_id,
|
||||
nat_gateway_id=nat_gateway_id,
|
||||
interface_id=interface_id,
|
||||
vpc_peering_connection_id=pcx_id,
|
||||
)
|
||||
@ -173,6 +175,10 @@ DESCRIBE_ROUTE_TABLES_RESPONSE = """
|
||||
<origin>CreateRoute</origin>
|
||||
<state>blackhole</state>
|
||||
{% endif %}
|
||||
{% if route.nat_gateway %}
|
||||
<natGatewayId>{{ route.nat_gateway.id }}</natGatewayId>
|
||||
<state>active</state>
|
||||
{% endif %}
|
||||
</item>
|
||||
{% endfor %}
|
||||
</routeSet>
|
||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.ec2.utils import filters_from_querystring
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
def try_parse_int(value, default=None):
|
||||
@ -171,12 +172,15 @@ DELETE_GROUP_RESPONSE = """<DeleteSecurityGroupResponse xmlns="http://ec2.amazon
|
||||
<return>true</return>
|
||||
</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>
|
||||
<securityGroupInfo>
|
||||
{% for group in groups %}
|
||||
<item>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<groupId>{{ group.id }}</groupId>
|
||||
<groupName>{{ group.name }}</groupName>
|
||||
<groupDescription>{{ group.description }}</groupDescription>
|
||||
@ -196,7 +200,9 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
|
||||
<groups>
|
||||
{% for source_group in rule.source_groups %}
|
||||
<item>
|
||||
<userId>123456789012</userId>
|
||||
<userId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</userId>
|
||||
<groupId>{{ source_group.id }}</groupId>
|
||||
<groupName>{{ source_group.name }}</groupName>
|
||||
</item>
|
||||
@ -225,7 +231,9 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
|
||||
<groups>
|
||||
{% for source_group in rule.source_groups %}
|
||||
<item>
|
||||
<userId>123456789012</userId>
|
||||
<userId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</userId>
|
||||
<groupId>{{ source_group.id }}</groupId>
|
||||
<groupName>{{ source_group.name }}</groupName>
|
||||
</item>
|
||||
@ -255,6 +263,7 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """<DescribeSecurityGroupsResponse xmlns="ht
|
||||
{% endfor %}
|
||||
</securityGroupInfo>
|
||||
</DescribeSecurityGroupsResponse>"""
|
||||
)
|
||||
|
||||
AUTHORIZE_SECURITY_GROUP_INGRESS_REPONSE = """<AuthorizeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||
|
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
class VPCPeeringConnections(BaseResponse):
|
||||
@ -40,7 +41,8 @@ class VPCPeeringConnections(BaseResponse):
|
||||
return template.render()
|
||||
|
||||
|
||||
CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
CREATE_VPC_PEERING_CONNECTION_RESPONSE = (
|
||||
"""
|
||||
<CreateVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<vpcPeeringConnection>
|
||||
@ -56,7 +58,9 @@ CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
</peeringOptions>
|
||||
</requesterVpcInfo>
|
||||
<accepterVpcInfo>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
||||
</accepterVpcInfo>
|
||||
<status>
|
||||
@ -68,8 +72,10 @@ CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
</vpcPeeringConnection>
|
||||
</CreateVpcPeeringConnectionResponse>
|
||||
"""
|
||||
)
|
||||
|
||||
DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
|
||||
DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = (
|
||||
"""
|
||||
<DescribeVpcPeeringConnectionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<vpcPeeringConnectionSet>
|
||||
@ -82,7 +88,9 @@ DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
|
||||
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
|
||||
</requesterVpcInfo>
|
||||
<accepterVpcInfo>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
||||
<cidrBlock>{{ vpc_pcx.peer_vpc.cidr_block }}</cidrBlock>
|
||||
<peeringOptions>
|
||||
@ -101,6 +109,7 @@ DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
|
||||
</vpcPeeringConnectionSet>
|
||||
</DescribeVpcPeeringConnectionsResponse>
|
||||
"""
|
||||
)
|
||||
|
||||
DELETE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
<DeleteVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||
@ -109,7 +118,8 @@ DELETE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
</DeleteVpcPeeringConnectionResponse>
|
||||
"""
|
||||
|
||||
ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = (
|
||||
"""
|
||||
<AcceptVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<vpcPeeringConnection>
|
||||
@ -120,7 +130,9 @@ ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
|
||||
</requesterVpcInfo>
|
||||
<accepterVpcInfo>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<ownerId>"""
|
||||
+ ACCOUNT_ID
|
||||
+ """</ownerId>
|
||||
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
||||
<cidrBlock>{{ vpc_pcx.peer_vpc.cidr_block }}</cidrBlock>
|
||||
<peeringOptions>
|
||||
@ -137,6 +149,7 @@ ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
</vpcPeeringConnection>
|
||||
</AcceptVpcPeeringConnectionResponse>
|
||||
"""
|
||||
)
|
||||
|
||||
REJECT_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
<RejectVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
||||
|
@ -2,6 +2,6 @@ from __future__ import unicode_literals
|
||||
from .responses import EC2Response
|
||||
|
||||
|
||||
url_bases = ["https?://ec2.(.+).amazonaws.com(|.cn)"]
|
||||
url_bases = ["https?://ec2\.(.+)\.amazonaws\.com(|\.cn)"]
|
||||
|
||||
url_paths = {"{0}/": EC2Response.dispatch}
|
||||
|
4
moto/ec2_instance_connect/__init__.py
Normal file
4
moto/ec2_instance_connect/__init__.py
Normal 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)
|
11
moto/ec2_instance_connect/models.py
Normal file
11
moto/ec2_instance_connect/models.py
Normal 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()
|
9
moto/ec2_instance_connect/responses.py
Normal file
9
moto/ec2_instance_connect/responses.py
Normal 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}
|
||||
)
|
6
moto/ec2_instance_connect/urls.py
Normal file
6
moto/ec2_instance_connect/urls.py
Normal 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}
|
@ -3,9 +3,10 @@ import re
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from random import random, randint
|
||||
import boto3
|
||||
|
||||
import pytz
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.utils import unix_time
|
||||
@ -1302,7 +1303,10 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("ecs")
|
||||
ecs_backends = {
|
||||
region: EC2ContainerServiceBackend(region) for region in available_regions
|
||||
}
|
||||
ecs_backends = {}
|
||||
for region in Session().get_available_regions("ecs"):
|
||||
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)
|
||||
|
@ -2,8 +2,8 @@ from __future__ import unicode_literals
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
import boto.emr
|
||||
import pytz
|
||||
from boto3 import Session
|
||||
from dateutil.parser import parse as dtparse
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.emr.exceptions import EmrError
|
||||
@ -460,5 +460,9 @@ class ElasticMapReduceBackend(BaseBackend):
|
||||
|
||||
|
||||
emr_backends = {}
|
||||
for region in boto.emr.regions():
|
||||
emr_backends[region.name] = ElasticMapReduceBackend(region.name)
|
||||
for region in Session().get_available_regions("emr"):
|
||||
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)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
@ -362,5 +362,10 @@ class EventsBackend(BaseBackend):
|
||||
self.event_buses.pop(name, None)
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("events")
|
||||
events_backends = {region: EventsBackend(region) for region in available_regions}
|
||||
events_backends = {}
|
||||
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)
|
||||
|
@ -4,8 +4,8 @@ import hashlib
|
||||
|
||||
import datetime
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
import boto.glacier
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
from .utils import get_job_id
|
||||
@ -221,5 +221,9 @@ class GlacierBackend(BaseBackend):
|
||||
|
||||
|
||||
glacier_backends = {}
|
||||
for region in boto.glacier.regions():
|
||||
glacier_backends[region.name] = GlacierBackend(region)
|
||||
for region in Session().get_available_regions("glacier"):
|
||||
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)
|
||||
|
@ -14,7 +14,7 @@ from cryptography.hazmat.backends import default_backend
|
||||
from six.moves.urllib.parse import urlparse
|
||||
|
||||
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 (
|
||||
iso_8601_datetime_without_milliseconds,
|
||||
iso_8601_datetime_with_milliseconds,
|
||||
@ -45,8 +45,6 @@ from .utils import (
|
||||
random_policy_id,
|
||||
)
|
||||
|
||||
ACCOUNT_ID = 123456789012
|
||||
|
||||
|
||||
class MFADevice(object):
|
||||
"""MFA Device class."""
|
||||
@ -309,6 +307,7 @@ class Role(BaseModel):
|
||||
permissions_boundary,
|
||||
description,
|
||||
tags,
|
||||
max_session_duration,
|
||||
):
|
||||
self.id = role_id
|
||||
self.name = name
|
||||
@ -320,6 +319,7 @@ class Role(BaseModel):
|
||||
self.tags = tags
|
||||
self.description = description
|
||||
self.permissions_boundary = permissions_boundary
|
||||
self.max_session_duration = max_session_duration
|
||||
|
||||
@property
|
||||
def created_iso_8601(self):
|
||||
@ -338,6 +338,7 @@ class Role(BaseModel):
|
||||
permissions_boundary=properties.get("PermissionsBoundary", ""),
|
||||
description=properties.get("Description", ""),
|
||||
tags=properties.get("Tags", {}),
|
||||
max_session_duration=properties.get("MaxSessionDuration", 3600),
|
||||
)
|
||||
|
||||
policies = properties.get("Policies", [])
|
||||
@ -542,7 +543,7 @@ class Group(BaseModel):
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
def __init__(self, name, path=None):
|
||||
def __init__(self, name, path=None, tags=None):
|
||||
self.name = name
|
||||
self.id = random_resource_id()
|
||||
self.path = path if path else "/"
|
||||
@ -555,6 +556,7 @@ class User(BaseModel):
|
||||
self.password = None
|
||||
self.password_reset_required = False
|
||||
self.signing_certificates = {}
|
||||
self.tags = tags
|
||||
|
||||
@property
|
||||
def arn(self):
|
||||
@ -938,9 +940,10 @@ class IAMBackend(BaseBackend):
|
||||
role.description = role_description
|
||||
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.description = role_description
|
||||
role.max_session_duration = max_session_duration
|
||||
return role
|
||||
|
||||
def detach_role_policy(self, policy_arn, role_name):
|
||||
@ -1059,6 +1062,7 @@ class IAMBackend(BaseBackend):
|
||||
permissions_boundary,
|
||||
description,
|
||||
tags,
|
||||
max_session_duration,
|
||||
):
|
||||
role_id = random_resource_id()
|
||||
if permissions_boundary and not self.policy_arn_regex.match(
|
||||
@ -1084,6 +1088,7 @@ class IAMBackend(BaseBackend):
|
||||
permissions_boundary,
|
||||
description,
|
||||
clean_tags,
|
||||
max_session_duration,
|
||||
)
|
||||
self.roles[role_id] = role
|
||||
return role
|
||||
@ -1417,13 +1422,13 @@ class IAMBackend(BaseBackend):
|
||||
"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:
|
||||
raise IAMConflictException(
|
||||
"EntityAlreadyExists", "User {0} already exists".format(user_name)
|
||||
)
|
||||
|
||||
user = User(user_name, path)
|
||||
user = User(user_name, path, tags)
|
||||
self.users[user_name] = user
|
||||
return user
|
||||
|
||||
@ -1579,6 +1584,10 @@ class IAMBackend(BaseBackend):
|
||||
user = self.get_user(user_name)
|
||||
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):
|
||||
user = self.get_user(user_name)
|
||||
|
||||
|
@ -182,6 +182,7 @@ class IamResponse(BaseResponse):
|
||||
permissions_boundary = self._get_param("PermissionsBoundary")
|
||||
description = self._get_param("Description")
|
||||
tags = self._get_multi_param("Tags.member")
|
||||
max_session_duration = self._get_param("MaxSessionDuration", 3600)
|
||||
|
||||
role = iam_backend.create_role(
|
||||
role_name,
|
||||
@ -190,6 +191,7 @@ class IamResponse(BaseResponse):
|
||||
permissions_boundary,
|
||||
description,
|
||||
tags,
|
||||
max_session_duration,
|
||||
)
|
||||
template = self.response_template(CREATE_ROLE_TEMPLATE)
|
||||
return template.render(role=role)
|
||||
@ -258,7 +260,8 @@ class IamResponse(BaseResponse):
|
||||
def update_role(self):
|
||||
role_name = self._get_param("RoleName")
|
||||
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)
|
||||
return template.render(role=role)
|
||||
|
||||
@ -437,8 +440,8 @@ class IamResponse(BaseResponse):
|
||||
def create_user(self):
|
||||
user_name = self._get_param("UserName")
|
||||
path = self._get_param("Path")
|
||||
|
||||
user = iam_backend.create_user(user_name, path)
|
||||
tags = self._get_multi_param("Tags.member")
|
||||
user = iam_backend.create_user(user_name, path, tags)
|
||||
template = self.response_template(USER_TEMPLATE)
|
||||
return template.render(action="Create", user=user)
|
||||
|
||||
@ -535,6 +538,12 @@ class IamResponse(BaseResponse):
|
||||
template = self.response_template(LIST_USER_POLICIES_TEMPLATE)
|
||||
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):
|
||||
user_name = self._get_param("UserName")
|
||||
policy_name = self._get_param("PolicyName")
|
||||
@ -1189,9 +1198,12 @@ CREATE_ROLE_TEMPLATE = """<CreateRoleResponse xmlns="https://iam.amazonaws.com/d
|
||||
<Arn>{{ role.arn }}</Arn>
|
||||
<RoleName>{{ role.name }}</RoleName>
|
||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
||||
{% if role.description %}
|
||||
<Description>{{role.description}}</Description>
|
||||
{% endif %}
|
||||
<CreateDate>{{ role.created_iso_8601 }}</CreateDate>
|
||||
<RoleId>{{ role.id }}</RoleId>
|
||||
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
|
||||
{% if role.permissions_boundary %}
|
||||
<PermissionsBoundary>
|
||||
<PermissionsBoundaryType>PermissionsBoundaryPolicy</PermissionsBoundaryType>
|
||||
@ -1244,6 +1256,7 @@ UPDATE_ROLE_DESCRIPTION_TEMPLATE = """<UpdateRoleDescriptionResponse xmlns="http
|
||||
<Description>{{role.description}}</Description>
|
||||
<CreateDate>{{ role.created_iso_8601 }}</CreateDate>
|
||||
<RoleId>{{ role.id }}</RoleId>
|
||||
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
|
||||
{% if role.tags %}
|
||||
<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>
|
||||
<RoleName>{{ role.name }}</RoleName>
|
||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
||||
{% if role.description %}
|
||||
<Description>{{role.description}}</Description>
|
||||
{% endif %}
|
||||
<CreateDate>{{ role.created_iso_8601 }}</CreateDate>
|
||||
<RoleId>{{ role.id }}</RoleId>
|
||||
<MaxSessionDuration>{{ role.max_session_duration }}</MaxSessionDuration>
|
||||
{% if role.tags %}
|
||||
<Tags>
|
||||
{% for tag in role.get_tags() %}
|
||||
@ -1689,6 +1705,23 @@ LIST_USER_POLICIES_TEMPLATE = """<ListUserPoliciesResponse>
|
||||
</ResponseMetadata>
|
||||
</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>
|
||||
<CreateAccessKeyResult>
|
||||
<AccessKey>
|
||||
|
@ -9,7 +9,7 @@ import uuid
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from .exceptions import (
|
||||
@ -825,5 +825,10 @@ class IoTBackend(BaseBackend):
|
||||
return self.jobs[job_id]
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("iot")
|
||||
iot_backends = {region: IoTBackend(region) for region in available_regions}
|
||||
iot_backends = {}
|
||||
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)
|
||||
|
@ -1,8 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
import json
|
||||
import time
|
||||
import boto3
|
||||
import jsondiff
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.iot import iot_backends
|
||||
from .exceptions import (
|
||||
@ -205,5 +206,10 @@ class IoTDataPlaneBackend(BaseBackend):
|
||||
return None
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("iot-data")
|
||||
iotdata_backends = {region: IoTDataPlaneBackend(region) for region in available_regions}
|
||||
iotdata_backends = {}
|
||||
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)
|
||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
class ResourceNotFoundError(BadRequest):
|
||||
@ -23,14 +24,14 @@ class ResourceInUseError(BadRequest):
|
||||
class StreamNotFoundError(ResourceNotFoundError):
|
||||
def __init__(self, stream_name):
|
||||
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):
|
||||
def __init__(self, shard_id):
|
||||
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)
|
||||
)
|
||||
|
||||
|
||||
|
@ -2,7 +2,6 @@ from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
import time
|
||||
import boto.kinesis
|
||||
import re
|
||||
import six
|
||||
import itertools
|
||||
@ -10,9 +9,12 @@ import itertools
|
||||
from operator import attrgetter
|
||||
from hashlib import md5
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.utils import unix_time
|
||||
from moto.core import ACCOUNT_ID
|
||||
from .exceptions import (
|
||||
StreamNotFoundError,
|
||||
ShardNotFoundError,
|
||||
@ -133,7 +135,7 @@ class Stream(BaseModel):
|
||||
self.shard_count = shard_count
|
||||
self.creation_datetime = datetime.datetime.now()
|
||||
self.region = region
|
||||
self.account_number = "123456789012"
|
||||
self.account_number = ACCOUNT_ID
|
||||
self.shards = {}
|
||||
self.tags = {}
|
||||
self.status = "ACTIVE"
|
||||
@ -259,8 +261,8 @@ class DeliveryStream(BaseModel):
|
||||
|
||||
@property
|
||||
def arn(self):
|
||||
return "arn:aws:firehose:us-east-1:123456789012:deliverystream/{0}".format(
|
||||
self.name
|
||||
return "arn:aws:firehose:us-east-1:{1}:deliverystream/{0}".format(
|
||||
self.name, ACCOUNT_ID
|
||||
)
|
||||
|
||||
def destinations_to_dict(self):
|
||||
@ -529,5 +531,9 @@ class KinesisBackend(BaseBackend):
|
||||
|
||||
|
||||
kinesis_backends = {}
|
||||
for region in boto.kinesis.regions():
|
||||
kinesis_backends[region.name] = KinesisBackend()
|
||||
for region in Session().get_available_regions("kinesis"):
|
||||
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()
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import boto.kms
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.utils import iso_8601_datetime_without_milliseconds
|
||||
@ -284,5 +284,9 @@ class KmsBackend(BaseBackend):
|
||||
|
||||
|
||||
kms_backends = {}
|
||||
for region in boto.kms.regions():
|
||||
kms_backends[region.name] = KmsBackend()
|
||||
for region in Session().get_available_regions("kms"):
|
||||
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()
|
||||
|
@ -1,5 +1,6 @@
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend
|
||||
import boto.logs
|
||||
from moto.core.utils import unix_time_millis
|
||||
from .exceptions import (
|
||||
ResourceNotFoundException,
|
||||
@ -558,6 +559,10 @@ class LogsBackend(BaseBackend):
|
||||
log_group.untag(tags)
|
||||
|
||||
|
||||
logs_backends = {
|
||||
region.name: LogsBackend(region.name) for region in boto.logs.regions()
|
||||
}
|
||||
logs_backends = {}
|
||||
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)
|
||||
|
@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.ec2 import ec2_backends
|
||||
from moto.core import ACCOUNT_ID
|
||||
import uuid
|
||||
import datetime
|
||||
from random import choice
|
||||
@ -367,7 +368,7 @@ class Stack(BaseModel):
|
||||
self.id = "{0}".format(uuid.uuid4())
|
||||
self.layers = []
|
||||
self.apps = []
|
||||
self.account_number = "123456789012"
|
||||
self.account_number = ACCOUNT_ID
|
||||
self.created_at = datetime.datetime.utcnow()
|
||||
|
||||
def __eq__(self, other):
|
||||
|
@ -2,8 +2,10 @@ from __future__ import unicode_literals
|
||||
|
||||
import random
|
||||
import string
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
MASTER_ACCOUNT_ID = "123456789012"
|
||||
|
||||
MASTER_ACCOUNT_ID = ACCOUNT_ID
|
||||
MASTER_ACCOUNT_EMAIL = "master@example.com"
|
||||
DEFAULT_POLICY_ID = "p-FullAWSAccess"
|
||||
ORGANIZATION_ARN_FORMAT = "arn:aws:organizations::{0}:organization/{1}"
|
||||
|
@ -2,13 +2,14 @@ from __future__ import unicode_literals
|
||||
from xml.etree import ElementTree as ET
|
||||
import datetime
|
||||
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
from .resources import VOICE_DATA
|
||||
from .utils import make_arn_for_lexicon
|
||||
|
||||
DEFAULT_ACCOUNT_ID = 123456789012
|
||||
from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
|
||||
|
||||
|
||||
class Lexicon(BaseModel):
|
||||
@ -113,7 +114,10 @@ class PollyBackend(BaseBackend):
|
||||
self._lexicons[name] = lexicon
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("polly")
|
||||
polly_backends = {
|
||||
region: PollyBackend(region_name=region) for region in available_regions
|
||||
}
|
||||
polly_backends = {}
|
||||
for region in Session().get_available_regions("polly"):
|
||||
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)
|
||||
|
@ -5,7 +5,7 @@ import datetime
|
||||
import os
|
||||
|
||||
from collections import defaultdict
|
||||
import boto.rds2
|
||||
from boto3 import Session
|
||||
from jinja2 import Template
|
||||
from re import compile as re_compile
|
||||
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||
@ -1501,6 +1501,10 @@ class DBParameterGroup(object):
|
||||
return db_parameter_group
|
||||
|
||||
|
||||
rds2_backends = dict(
|
||||
(region.name, RDS2Backend(region.name)) for region in boto.rds2.regions()
|
||||
)
|
||||
rds2_backends = {}
|
||||
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)
|
||||
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
import boto.redshift
|
||||
from boto3 import Session
|
||||
from botocore.exceptions import ClientError
|
||||
from moto.compat import OrderedDict
|
||||
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):
|
||||
@ -897,7 +897,9 @@ class RedshiftBackend(BaseBackend):
|
||||
|
||||
|
||||
redshift_backends = {}
|
||||
for region in boto.redshift.regions():
|
||||
redshift_backends[region.name] = RedshiftBackend(
|
||||
ec2_backends[region.name], region.name
|
||||
)
|
||||
for region in Session().get_available_regions("redshift"):
|
||||
redshift_backends[region] = RedshiftBackend(ec2_backends[region], region)
|
||||
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)
|
||||
|
@ -1,11 +1,13 @@
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
|
||||
import boto3
|
||||
import json
|
||||
import re
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import ACCOUNT_ID
|
||||
from .exceptions import BadRequestException
|
||||
|
||||
|
||||
@ -23,8 +25,8 @@ class FakeResourceGroup(BaseModel):
|
||||
if self._validate_tags(value=tags):
|
||||
self._tags = tags
|
||||
self._raise_errors()
|
||||
self.arn = "arn:aws:resource-groups:us-west-1:123456789012:{name}".format(
|
||||
name=name
|
||||
self.arn = "arn:aws:resource-groups:us-west-1:{AccountId}:{name}".format(
|
||||
name=name, AccountId=ACCOUNT_ID
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@ -349,7 +351,14 @@ class ResourceGroupsBackend(BaseBackend):
|
||||
return self.groups.by_name[group_name]
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("resource-groups")
|
||||
resourcegroups_backends = {
|
||||
region: ResourceGroupsBackend(region_name=region) for region in available_regions
|
||||
}
|
||||
resourcegroups_backends = {}
|
||||
for region in Session().get_available_regions("resource-groups"):
|
||||
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)
|
||||
|
@ -1,7 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import uuid
|
||||
import boto3
|
||||
import six
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend
|
||||
from moto.core.exceptions import RESTError
|
||||
|
||||
@ -636,9 +637,14 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
|
||||
# return failed_resources_map
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions(
|
||||
"resourcegroupstaggingapi"
|
||||
)
|
||||
resourcegroupstaggingapi_backends = {
|
||||
region: ResourceGroupsTaggingAPIBackend(region) for region in available_regions
|
||||
}
|
||||
resourcegroupstaggingapi_backends = {}
|
||||
for region in Session().get_available_regions("resourcegroupstaggingapi"):
|
||||
resourcegroupstaggingapi_backends[region] = ResourceGroupsTaggingAPIBackend(region)
|
||||
for region in Session().get_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)
|
||||
|
@ -323,3 +323,27 @@ class BucketSignatureDoesNotMatchError(S3ClientError):
|
||||
*args,
|
||||
**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
|
||||
)
|
||||
|
@ -35,6 +35,8 @@ from .exceptions import (
|
||||
InvalidTargetBucketForLogging,
|
||||
DuplicateTagKeys,
|
||||
CrossLocationLoggingProhibitted,
|
||||
NoSuchPublicAccessBlockConfiguration,
|
||||
InvalidPublicAccessBlockConfiguration,
|
||||
)
|
||||
from .utils import clean_key_name, _VersionedKeyStore
|
||||
|
||||
@ -659,11 +661,8 @@ class Notification(BaseModel):
|
||||
else:
|
||||
data["filter"] = None
|
||||
|
||||
data[
|
||||
"objectPrefixes"
|
||||
] = (
|
||||
[]
|
||||
) # Not sure why this is a thing since AWS just seems to return this as filters ¯\_(ツ)_/¯
|
||||
# Not sure why this is a thing since AWS just seems to return this as filters ¯\_(ツ)_/¯
|
||||
data["objectPrefixes"] = []
|
||||
|
||||
return data
|
||||
|
||||
@ -728,6 +727,38 @@ class NotificationConfiguration(BaseModel):
|
||||
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):
|
||||
def __init__(self, name, region_name):
|
||||
self.name = name
|
||||
@ -746,6 +777,7 @@ class FakeBucket(BaseModel):
|
||||
self.accelerate_configuration = None
|
||||
self.payer = "BucketOwner"
|
||||
self.creation_date = datetime.datetime.utcnow()
|
||||
self.public_access_block = None
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
@ -1079,13 +1111,16 @@ class FakeBucket(BaseModel):
|
||||
}
|
||||
|
||||
# Make the supplementary configuration:
|
||||
# TODO: Implement Public Access Block Support
|
||||
|
||||
# This is a dobule-wrapped JSON for some reason...
|
||||
s_config = {
|
||||
"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:
|
||||
if config_dict["tags"]:
|
||||
s_config["BucketTaggingConfiguration"] = json.dumps(
|
||||
@ -1221,6 +1256,14 @@ class S3Backend(BaseBackend):
|
||||
bucket = self.get_bucket(bucket_name)
|
||||
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(
|
||||
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.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):
|
||||
bucket = self.get_bucket(bucket_name)
|
||||
bucket.set_notification_configuration(notification_config)
|
||||
@ -1324,6 +1371,19 @@ class S3Backend(BaseBackend):
|
||||
raise InvalidRequest("PutBucketAccelerateConfiguration")
|
||||
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):
|
||||
bucket = self.get_bucket(bucket_name)
|
||||
new_multipart = FakeMultipart(key_name, metadata)
|
||||
|
@ -1,10 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
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
|
||||
|
||||
import xmltodict
|
||||
@ -12,6 +13,7 @@ import xmltodict
|
||||
from moto.packages.httpretty.core import HTTPrettyRequest
|
||||
from moto.core.responses import _TemplateEnvironmentMixin, ActionAuthenticatorMixin
|
||||
from moto.core.utils import path_url
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
from moto.s3bucket_path.utils import (
|
||||
bucket_name_from_url as bucketpath_bucket_name_from_url,
|
||||
@ -70,6 +72,7 @@ ACTION_MAP = {
|
||||
"notification": "GetBucketNotification",
|
||||
"accelerate": "GetAccelerateConfiguration",
|
||||
"versions": "ListBucketVersions",
|
||||
"public_access_block": "GetPublicAccessBlock",
|
||||
"DEFAULT": "ListBucket",
|
||||
},
|
||||
"PUT": {
|
||||
@ -83,6 +86,7 @@ ACTION_MAP = {
|
||||
"cors": "PutBucketCORS",
|
||||
"notification": "PutBucketNotification",
|
||||
"accelerate": "PutAccelerateConfiguration",
|
||||
"public_access_block": "PutPublicAccessBlock",
|
||||
"DEFAULT": "CreateBucket",
|
||||
},
|
||||
"DELETE": {
|
||||
@ -90,6 +94,7 @@ ACTION_MAP = {
|
||||
"policy": "DeleteBucketPolicy",
|
||||
"tagging": "PutBucketTagging",
|
||||
"cors": "PutBucketCORS",
|
||||
"public_access_block": "DeletePublicAccessBlock",
|
||||
"DEFAULT": "DeleteBucket",
|
||||
},
|
||||
},
|
||||
@ -399,6 +404,12 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
return 200, {}, template.render()
|
||||
template = self.response_template(S3_BUCKET_ACCELERATE)
|
||||
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:
|
||||
delimiter = querystring.get("delimiter", [None])[0]
|
||||
@ -651,6 +662,23 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
except Exception as 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:
|
||||
if body:
|
||||
# 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.delete_lifecycle()
|
||||
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)
|
||||
|
||||
@ -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"?>
|
||||
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Name>{{ bucket.name }}</Name>
|
||||
{% if prefix != None %}
|
||||
<Prefix>{{ prefix }}</Prefix>
|
||||
{% endif %}
|
||||
<MaxKeys>{{ max_keys }}</MaxKeys>
|
||||
<Delimiter>{{ delimiter }}</Delimiter>
|
||||
<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"?>
|
||||
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Name>{{ bucket.name }}</Name>
|
||||
{% if prefix != None %}
|
||||
<Prefix>{{ prefix }}</Prefix>
|
||||
{% endif %}
|
||||
<MaxKeys>{{ max_keys }}</MaxKeys>
|
||||
<KeyCount>{{ key_count }}</KeyCount>
|
||||
{% 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"?>
|
||||
<ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
|
||||
<Name>{{ bucket.name }}</Name>
|
||||
{% if prefix != None %}
|
||||
<Prefix>{{ prefix }}</Prefix>
|
||||
{% endif %}
|
||||
<KeyMarker>{{ key_marker }}</KeyMarker>
|
||||
<MaxKeys>{{ max_keys }}</MaxKeys>
|
||||
<IsTruncated>{{ is_truncated }}</IsTruncated>
|
||||
@ -1856,7 +1893,8 @@ S3_MULTIPART_COMPLETE_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
</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/">
|
||||
<Bucket>{{ bucket_name }}</Bucket>
|
||||
<KeyMarker></KeyMarker>
|
||||
@ -1868,7 +1906,9 @@ S3_ALL_MULTIPARTS = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Key>{{ upload.key_name }}</Key>
|
||||
<UploadId>{{ upload.id }}</UploadId>
|
||||
<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>
|
||||
</Initiator>
|
||||
<Owner>
|
||||
@ -1881,6 +1921,7 @@ S3_ALL_MULTIPARTS = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
{% endfor %}
|
||||
</ListMultipartUploadsResult>
|
||||
"""
|
||||
)
|
||||
|
||||
S3_NO_POLICY = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
@ -2053,3 +2094,12 @@ S3_BUCKET_ACCELERATE = """
|
||||
S3_BUCKET_ACCELERATE_NOT_SET = """
|
||||
<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>
|
||||
"""
|
||||
|
@ -6,7 +6,7 @@ import json
|
||||
import uuid
|
||||
import datetime
|
||||
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from .exceptions import (
|
||||
@ -491,7 +491,14 @@ class SecretsManagerBackend(BaseBackend):
|
||||
)
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("secretsmanager")
|
||||
secretsmanager_backends = {
|
||||
region: SecretsManagerBackend(region_name=region) for region in available_regions
|
||||
}
|
||||
secretsmanager_backends = {}
|
||||
for region in Session().get_available_regions("secretsmanager"):
|
||||
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)
|
||||
|
@ -1,3 +1,5 @@
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
"""
|
||||
SES Feedback messages
|
||||
Extracted from https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html
|
||||
@ -10,7 +12,7 @@ COMMON_MAIL = {
|
||||
"source": "sender@example.com",
|
||||
"sourceArn": "arn:aws:ses:us-west-2:888888888888:identity/example.com",
|
||||
"sourceIp": "127.0.3.0",
|
||||
"sendingAccountId": "123456789012",
|
||||
"sendingAccountId": ACCOUNT_ID,
|
||||
"destination": ["recipient@example.com"],
|
||||
"headersTruncated": False,
|
||||
"headers": [
|
||||
|
@ -31,7 +31,8 @@ from .exceptions import (
|
||||
)
|
||||
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
|
||||
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
|
||||
|
||||
@ -259,7 +260,9 @@ class Subscription(BaseModel):
|
||||
"SignatureVersion": "1",
|
||||
"Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",
|
||||
"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:
|
||||
post_data["MessageAttributes"] = message_attributes
|
||||
@ -275,8 +278,11 @@ class PlatformApplication(BaseModel):
|
||||
|
||||
@property
|
||||
def arn(self):
|
||||
return "arn:aws:sns:{region}:123456789012:app/{platform}/{name}".format(
|
||||
region=self.region, platform=self.platform, name=self.name
|
||||
return "arn:aws:sns:{region}:{AccountId}:app/{platform}/{name}".format(
|
||||
region=self.region,
|
||||
platform=self.platform,
|
||||
name=self.name,
|
||||
AccountId=DEFAULT_ACCOUNT_ID,
|
||||
)
|
||||
|
||||
|
||||
@ -305,8 +311,9 @@ class PlatformEndpoint(BaseModel):
|
||||
|
||||
@property
|
||||
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,
|
||||
AccountId=DEFAULT_ACCOUNT_ID,
|
||||
platform=self.application.platform,
|
||||
name=self.application.name,
|
||||
id=self.id,
|
||||
@ -390,10 +397,6 @@ class SNSBackend(BaseBackend):
|
||||
return self._get_values_nexttoken(self.topics, next_token)
|
||||
|
||||
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)
|
||||
|
||||
def get_topic(self, arn):
|
||||
@ -432,11 +435,18 @@ class SNSBackend(BaseBackend):
|
||||
subscription = Subscription(topic, endpoint, protocol)
|
||||
attributes = {
|
||||
"PendingConfirmation": "false",
|
||||
"ConfirmationWasAuthenticated": "true",
|
||||
"Endpoint": endpoint,
|
||||
"TopicArn": topic_arn,
|
||||
"Protocol": protocol,
|
||||
"SubscriptionArn": subscription.arn,
|
||||
"Owner": DEFAULT_ACCOUNT_ID,
|
||||
"RawMessageDelivery": "false",
|
||||
}
|
||||
|
||||
if protocol in ["http", "https"]:
|
||||
attributes["EffectiveDeliveryPolicy"] = topic.effective_delivery_policy
|
||||
|
||||
subscription.attributes = attributes
|
||||
self.subscriptions[subscription.arn] = subscription
|
||||
return subscription
|
||||
@ -452,7 +462,7 @@ class SNSBackend(BaseBackend):
|
||||
return None
|
||||
|
||||
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):
|
||||
if topic_arn:
|
||||
@ -693,21 +703,25 @@ class SNSBackend(BaseBackend):
|
||||
sns_backends = {}
|
||||
for region in Session().get_available_regions("sns"):
|
||||
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 = {
|
||||
"http": {
|
||||
"disableSubscriptionOverrides": False,
|
||||
"defaultHealthyRetryPolicy": {
|
||||
"numNoDelayRetries": 0,
|
||||
"numMinDelayRetries": 0,
|
||||
"minDelayTarget": 20,
|
||||
"maxDelayTarget": 20,
|
||||
"numMaxDelayRetries": 0,
|
||||
"numRetries": 3,
|
||||
"backoffFunction": "linear",
|
||||
},
|
||||
}
|
||||
"defaultHealthyRetryPolicy": {
|
||||
"numNoDelayRetries": 0,
|
||||
"numMinDelayRetries": 0,
|
||||
"minDelayTarget": 20,
|
||||
"maxDelayTarget": 20,
|
||||
"numMaxDelayRetries": 0,
|
||||
"numRetries": 3,
|
||||
"backoffFunction": "linear",
|
||||
},
|
||||
"sicklyRetryPolicy": None,
|
||||
"throttlePolicy": None,
|
||||
"guaranteed": False,
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ import six
|
||||
import struct
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
import boto.sqs
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
@ -32,7 +32,8 @@ from .exceptions import (
|
||||
InvalidAttributeName,
|
||||
)
|
||||
|
||||
DEFAULT_ACCOUNT_ID = 123456789012
|
||||
from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
|
||||
|
||||
DEFAULT_SENDER_ID = "AIDAIT2UOQQY3AUEKVGXU"
|
||||
|
||||
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
|
||||
@ -417,8 +418,8 @@ class Queue(BaseModel):
|
||||
return result
|
||||
|
||||
def url(self, request_url):
|
||||
return "{0}://{1}/123456789012/{2}".format(
|
||||
request_url.scheme, request_url.netloc, self.name
|
||||
return "{0}://{1}/{2}/{3}".format(
|
||||
request_url.scheme, request_url.netloc, DEFAULT_ACCOUNT_ID, self.name
|
||||
)
|
||||
|
||||
@property
|
||||
@ -856,5 +857,9 @@ class SQSBackend(BaseBackend):
|
||||
|
||||
|
||||
sqs_backends = {}
|
||||
for region in boto.sqs.regions():
|
||||
sqs_backends[region.name] = SQSBackend(region.name)
|
||||
for region in Session().get_available_regions("sqs"):
|
||||
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)
|
||||
|
@ -13,6 +13,7 @@ import time
|
||||
import uuid
|
||||
import itertools
|
||||
|
||||
from .utils import parameter_arn
|
||||
from .exceptions import (
|
||||
ValidationException,
|
||||
InvalidFilterValue,
|
||||
@ -60,19 +61,23 @@ class Parameter(BaseModel):
|
||||
if value.startswith(prefix):
|
||||
return value[len(prefix) :]
|
||||
|
||||
def response_object(self, decrypt=False):
|
||||
def response_object(self, decrypt=False, region=None):
|
||||
r = {
|
||||
"Name": self.name,
|
||||
"Type": self.type,
|
||||
"Value": self.decrypt(self.value) if decrypt else self.value,
|
||||
"Version": self.version,
|
||||
"LastModifiedDate": round(self.last_modified_date, 3),
|
||||
}
|
||||
|
||||
if region:
|
||||
r["ARN"] = parameter_arn(region, self.name)
|
||||
|
||||
return r
|
||||
|
||||
def describe_response_object(self, decrypt=False):
|
||||
r = self.response_object(decrypt)
|
||||
r["LastModifiedDate"] = int(self.last_modified_date)
|
||||
r["LastModifiedDate"] = round(self.last_modified_date, 3)
|
||||
r["LastModifiedUser"] = "N/A"
|
||||
|
||||
if self.description:
|
||||
@ -510,7 +515,15 @@ class SimpleSystemManagerBackend(BaseBackend):
|
||||
result.append(self.get_parameter(name, with_decryption))
|
||||
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."""
|
||||
result = []
|
||||
# path could be with or without a trailing /. we handle this
|
||||
@ -527,7 +540,19 @@ class SimpleSystemManagerBackend(BaseBackend):
|
||||
continue
|
||||
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):
|
||||
if name in self._parameters:
|
||||
|
@ -51,7 +51,7 @@ class SimpleSystemManagerResponse(BaseResponse):
|
||||
}
|
||||
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)
|
||||
|
||||
def get_parameters(self):
|
||||
@ -63,7 +63,7 @@ class SimpleSystemManagerResponse(BaseResponse):
|
||||
response = {"Parameters": [], "InvalidParameters": []}
|
||||
|
||||
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)
|
||||
|
||||
param_names = [param.name for param in result]
|
||||
@ -77,15 +77,22 @@ class SimpleSystemManagerResponse(BaseResponse):
|
||||
with_decryption = self._get_param("WithDecryption")
|
||||
recursive = self._get_param("Recursive", False)
|
||||
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(
|
||||
path, with_decryption, recursive, filters
|
||||
result, next_token = self.ssm_backend.get_parameters_by_path(
|
||||
path,
|
||||
with_decryption,
|
||||
recursive,
|
||||
filters,
|
||||
next_token=token,
|
||||
max_results=max_results,
|
||||
)
|
||||
|
||||
response = {"Parameters": []}
|
||||
response = {"Parameters": [], "NextToken": next_token}
|
||||
|
||||
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)
|
||||
|
||||
return json.dumps(response)
|
||||
|
9
moto/ssm/utils.py
Normal file
9
moto/ssm/utils.py
Normal 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
|
||||
)
|
@ -1,6 +1,8 @@
|
||||
import boto
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend
|
||||
from moto.core.utils import iso_8601_datetime_without_milliseconds
|
||||
from moto.sts.models import ACCOUNT_ID
|
||||
@ -280,7 +282,12 @@ class StepFunctionBackend(BaseBackend):
|
||||
return ACCOUNT_ID
|
||||
|
||||
|
||||
stepfunction_backends = {
|
||||
_region.name: StepFunctionBackend(_region.name)
|
||||
for _region in boto.awslambda.regions()
|
||||
}
|
||||
stepfunction_backends = {}
|
||||
for region in Session().get_available_regions("stepfunctions"):
|
||||
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)
|
||||
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
||||
import datetime
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
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 (
|
||||
random_access_key_id,
|
||||
random_secret_access_key,
|
||||
|
@ -1,7 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
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 .exceptions import STSValidationError
|
||||
from .models import sts_backend
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import boto.swf
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend
|
||||
|
||||
@ -418,5 +418,9 @@ class SWFBackend(BaseBackend):
|
||||
|
||||
|
||||
swf_backends = {}
|
||||
for region in boto.swf.regions():
|
||||
swf_backends[region.name] = SWFBackend(region.name)
|
||||
for region in Session().get_available_regions("swf"):
|
||||
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)
|
||||
|
@ -2,8 +2,9 @@
|
||||
mock
|
||||
nose
|
||||
black; python_version >= '3.6'
|
||||
regex==2019.11.1; python_version >= '3.6' # Needed for black
|
||||
sure==1.4.11
|
||||
coverage
|
||||
coverage==4.5.4
|
||||
flake8==3.7.8
|
||||
freezegun
|
||||
flask
|
||||
|
@ -1,5 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
import boto3
|
||||
from boto3 import Session
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
|
||||
|
||||
@ -16,5 +16,10 @@ class {{ service_class }}Backend(BaseBackend):
|
||||
# add methods from here
|
||||
|
||||
|
||||
available_regions = boto3.session.Session().get_available_regions("{{ service }}")
|
||||
{{ escaped_service }}_backends = {region: {{ service_class }}Backend(region) for region in available_regions}
|
||||
{{ escaped_service }}_backends = {}
|
||||
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()
|
||||
|
@ -9,6 +9,7 @@ import uuid
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
from moto import mock_acm
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
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_CRT_BAD = _GET_RESOURCE("star_moto_com-bad.pem")
|
||||
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):
|
||||
|
@ -9,6 +9,7 @@ from botocore.exceptions import ClientError
|
||||
|
||||
import responses
|
||||
from moto import mock_apigateway, settings
|
||||
from moto.core import ACCOUNT_ID
|
||||
from nose.tools import assert_raises
|
||||
|
||||
|
||||
@ -881,7 +882,9 @@ def test_put_integration_validation():
|
||||
client.put_integration(
|
||||
restApiId=api_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",
|
||||
type=type,
|
||||
uri="arn:aws:apigateway:us-west-2:s3:path/b/k",
|
||||
@ -903,7 +906,9 @@ def test_put_integration_validation():
|
||||
client.put_integration(
|
||||
restApiId=api_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",
|
||||
type=type,
|
||||
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")
|
||||
|
||||
|
||||
@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
|
||||
def test_api_keys():
|
||||
region_name = "us-west-2"
|
||||
@ -1139,26 +1161,18 @@ def test_api_keys():
|
||||
|
||||
apikey_value = "12345"
|
||||
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)
|
||||
apikey_id = response["id"]
|
||||
apikey = client.get_api_key(apiKey=response["id"])
|
||||
apikey["name"].should.equal(apikey_name)
|
||||
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"]["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 = [
|
||||
{"op": "replace", "path": "/name", "value": "TESTKEY3_CHANGE"},
|
||||
@ -1172,13 +1186,25 @@ def test_api_keys():
|
||||
response["description"].should.equal("APIKEY UPDATE TEST")
|
||||
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()
|
||||
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)
|
||||
|
||||
response = client.get_api_keys()
|
||||
len(response["items"]).should.equal(2)
|
||||
len(response["items"]).should.equal(1)
|
||||
|
||||
|
||||
@mock_apigateway
|
||||
|
@ -8,6 +8,7 @@ import sure # noqa
|
||||
|
||||
from moto import mock_autoscaling_deprecated
|
||||
from moto import mock_autoscaling
|
||||
from moto.core import ACCOUNT_ID
|
||||
from tests.helpers import requires_boto_gte
|
||||
|
||||
|
||||
@ -22,7 +23,9 @@ def test_create_launch_configuration():
|
||||
security_groups=["default", "default2"],
|
||||
user_data=b"This is some user_data",
|
||||
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,
|
||||
)
|
||||
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.instance_monitoring.enabled.should.equal("true")
|
||||
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)
|
||||
|
||||
@ -71,7 +74,9 @@ def test_create_launch_configuration_with_block_device_mappings():
|
||||
security_groups=["default", "default2"],
|
||||
user_data=b"This is some user_data",
|
||||
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,
|
||||
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.instance_monitoring.enabled.should.equal("true")
|
||||
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)
|
||||
len(launch_config.block_device_mappings).should.equal(3)
|
||||
|
@ -306,8 +306,8 @@ def test_create_function_from_aws_bucket():
|
||||
result.should.equal(
|
||||
{
|
||||
"FunctionName": "testFunction",
|
||||
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
|
||||
_lambda_region
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
|
||||
_lambda_region, ACCOUNT_ID
|
||||
),
|
||||
"Runtime": "python2.7",
|
||||
"Role": result["Role"],
|
||||
@ -353,8 +353,8 @@ def test_create_function_from_zipfile():
|
||||
result.should.equal(
|
||||
{
|
||||
"FunctionName": "testFunction",
|
||||
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
|
||||
_lambda_region
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
|
||||
_lambda_region, ACCOUNT_ID
|
||||
),
|
||||
"Runtime": "python2.7",
|
||||
"Role": result["Role"],
|
||||
@ -431,7 +431,7 @@ def test_get_function():
|
||||
result = conn.get_function(FunctionName="testFunction", Qualifier="$LATEST")
|
||||
result["Configuration"]["Version"].should.equal("$LATEST")
|
||||
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
|
||||
@ -620,8 +620,8 @@ def test_list_create_list_get_delete_list():
|
||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
||||
"CodeSize": len(zip_content),
|
||||
"Description": "test lambda function",
|
||||
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
|
||||
_lambda_region
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
|
||||
_lambda_region, ACCOUNT_ID
|
||||
),
|
||||
"FunctionName": "testFunction",
|
||||
"Handler": "lambda_function.lambda_handler",
|
||||
@ -749,16 +749,17 @@ def test_tags_not_found():
|
||||
"""
|
||||
conn = boto3.client("lambda", "us-west-2")
|
||||
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)
|
||||
|
||||
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"),
|
||||
).should.throw(botocore.client.ClientError)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@ -815,8 +816,8 @@ def test_get_function_created_with_zipfile():
|
||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
||||
"CodeSize": len(zip_content),
|
||||
"Description": "test lambda function",
|
||||
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
|
||||
_lambda_region
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
|
||||
_lambda_region, ACCOUNT_ID
|
||||
),
|
||||
"FunctionName": "testFunction",
|
||||
"Handler": "lambda_function.handler",
|
||||
@ -921,18 +922,15 @@ def test_list_versions_by_function():
|
||||
assert res["ResponseMetadata"]["HTTPStatusCode"] == 201
|
||||
versions = conn.list_versions_by_function(FunctionName="testFunction")
|
||||
assert len(versions["Versions"]) == 3
|
||||
assert (
|
||||
versions["Versions"][0]["FunctionArn"]
|
||||
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction:$LATEST"
|
||||
)
|
||||
assert (
|
||||
versions["Versions"][1]["FunctionArn"]
|
||||
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction:1"
|
||||
)
|
||||
assert (
|
||||
versions["Versions"][2]["FunctionArn"]
|
||||
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction:2"
|
||||
)
|
||||
assert versions["Versions"][0][
|
||||
"FunctionArn"
|
||||
] == "arn:aws:lambda:us-west-2:{}:function:testFunction:$LATEST".format(ACCOUNT_ID)
|
||||
assert versions["Versions"][1][
|
||||
"FunctionArn"
|
||||
] == "arn:aws:lambda:us-west-2:{}:function:testFunction:1".format(ACCOUNT_ID)
|
||||
assert versions["Versions"][2][
|
||||
"FunctionArn"
|
||||
] == "arn:aws:lambda:us-west-2:{}:function:testFunction:2".format(ACCOUNT_ID)
|
||||
|
||||
conn.create_function(
|
||||
FunctionName="testFunction_2",
|
||||
@ -947,9 +945,10 @@ def test_list_versions_by_function():
|
||||
)
|
||||
versions = conn.list_versions_by_function(FunctionName="testFunction_2")
|
||||
assert len(versions["Versions"]) == 1
|
||||
assert (
|
||||
versions["Versions"][0]["FunctionArn"]
|
||||
== "arn:aws:lambda:us-west-2:123456789012:function:testFunction_2:$LATEST"
|
||||
assert versions["Versions"][0][
|
||||
"FunctionArn"
|
||||
] == "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(),
|
||||
"CodeSize": len(zip_content_two),
|
||||
"Description": "test lambda function",
|
||||
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunctionZip:2".format(
|
||||
_lambda_region
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionZip:2".format(
|
||||
_lambda_region, ACCOUNT_ID
|
||||
),
|
||||
"FunctionName": "testFunctionZip",
|
||||
"Handler": "lambda_function.lambda_handler",
|
||||
@ -1488,8 +1487,8 @@ def test_update_function_s3():
|
||||
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
|
||||
"CodeSize": len(zip_content_two),
|
||||
"Description": "test lambda function",
|
||||
"FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunctionS3:2".format(
|
||||
_lambda_region
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionS3:2".format(
|
||||
_lambda_region, ACCOUNT_ID
|
||||
),
|
||||
"FunctionName": "testFunctionS3",
|
||||
"Handler": "lambda_function.lambda_handler",
|
||||
|
@ -55,6 +55,10 @@ def _setup(ec2_client, iam_client):
|
||||
RoleName="TestRole", AssumeRolePolicyDocument="some_policy"
|
||||
)
|
||||
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
|
||||
|
||||
@ -83,7 +87,7 @@ def test_create_managed_compute_environment():
|
||||
"subnets": [subnet_id],
|
||||
"securityGroupIds": [sg_id],
|
||||
"ec2KeyPair": "string",
|
||||
"instanceRole": iam_arn,
|
||||
"instanceRole": iam_arn.replace("role", "instance-profile"),
|
||||
"tags": {"string": "string"},
|
||||
"bidPercentage": 123,
|
||||
"spotIamFleetRole": "string",
|
||||
@ -209,7 +213,7 @@ def test_delete_managed_compute_environment():
|
||||
"subnets": [subnet_id],
|
||||
"securityGroupIds": [sg_id],
|
||||
"ec2KeyPair": "string",
|
||||
"instanceRole": iam_arn,
|
||||
"instanceRole": iam_arn.replace("role", "instance-profile"),
|
||||
"tags": {"string": "string"},
|
||||
"bidPercentage": 123,
|
||||
"spotIamFleetRole": "string",
|
||||
@ -608,6 +612,9 @@ def test_describe_task_definition():
|
||||
resp = batch_client.describe_job_definitions(jobDefinitions=["sleep10", "test1"])
|
||||
len(resp["jobDefinitions"]).should.equal(3)
|
||||
|
||||
for job_definition in resp["jobDefinitions"]:
|
||||
job_definition["status"].should.equal("ACTIVE")
|
||||
|
||||
|
||||
@mock_logs
|
||||
@mock_ec2
|
||||
|
@ -51,6 +51,10 @@ def _setup(ec2_client, iam_client):
|
||||
RoleName="TestRole", AssumeRolePolicyDocument="some_policy"
|
||||
)
|
||||
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
|
||||
|
||||
@ -78,7 +82,7 @@ def test_create_env_cf():
|
||||
"InstanceTypes": ["optimal"],
|
||||
"Subnets": [subnet_id],
|
||||
"SecurityGroupIds": [sg_id],
|
||||
"InstanceRole": iam_arn,
|
||||
"InstanceRole": iam_arn.replace("role", "instance-profile"),
|
||||
},
|
||||
"ServiceRole": iam_arn,
|
||||
},
|
||||
@ -129,7 +133,7 @@ def test_create_job_queue_cf():
|
||||
"InstanceTypes": ["optimal"],
|
||||
"Subnets": [subnet_id],
|
||||
"SecurityGroupIds": [sg_id],
|
||||
"InstanceRole": iam_arn,
|
||||
"InstanceRole": iam_arn.replace("role", "instance-profile"),
|
||||
},
|
||||
"ServiceRole": iam_arn,
|
||||
},
|
||||
@ -195,7 +199,7 @@ def test_create_job_def_cf():
|
||||
"InstanceTypes": ["optimal"],
|
||||
"Subnets": [subnet_id],
|
||||
"SecurityGroupIds": [sg_id],
|
||||
"InstanceRole": iam_arn,
|
||||
"InstanceRole": iam_arn.replace("role", "instance-profile"),
|
||||
},
|
||||
"ServiceRole": iam_arn,
|
||||
},
|
||||
|
@ -14,6 +14,7 @@ import sure # noqa
|
||||
# Ensure 'assert_raises' context manager support for Python 2.6
|
||||
import tests.backport_assert_raises # noqa
|
||||
from nose.tools import assert_raises
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
from moto import (
|
||||
mock_cloudformation_deprecated,
|
||||
@ -129,12 +130,12 @@ def test_create_stack_with_notification_arn():
|
||||
conn.create_stack(
|
||||
"test_stack_with_notifications",
|
||||
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]
|
||||
[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)
|
||||
)
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@ import sure # noqa
|
||||
from nose.tools import assert_raises
|
||||
|
||||
from moto import mock_cloudformation, mock_s3, mock_sqs, mock_ec2
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
dummy_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
@ -174,17 +175,17 @@ def test_boto3_describe_stack_instances():
|
||||
)
|
||||
cf_conn.create_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-2"],
|
||||
)
|
||||
usw2_instance = cf_conn.describe_stack_instance(
|
||||
StackSetName="test_stack_set",
|
||||
StackInstanceAccount="123456789012",
|
||||
StackInstanceAccount=ACCOUNT_ID,
|
||||
StackInstanceRegion="us-west-2",
|
||||
)
|
||||
use1_instance = cf_conn.describe_stack_instance(
|
||||
StackSetName="test_stack_set",
|
||||
StackInstanceAccount="123456789012",
|
||||
StackInstanceAccount=ACCOUNT_ID,
|
||||
StackInstanceRegion="us-east-1",
|
||||
)
|
||||
|
||||
@ -192,13 +193,13 @@ def test_boto3_describe_stack_instances():
|
||||
"us-west-2"
|
||||
)
|
||||
usw2_instance["StackInstance"].should.have.key("Account").which.should.equal(
|
||||
"123456789012"
|
||||
ACCOUNT_ID
|
||||
)
|
||||
use1_instance["StackInstance"].should.have.key("Region").which.should.equal(
|
||||
"us-east-1"
|
||||
)
|
||||
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(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-1", "us-west-2"],
|
||||
)
|
||||
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(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-1", "us-west-2"],
|
||||
)
|
||||
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(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-1", "us-west-2"],
|
||||
)
|
||||
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"][0].should.have.key("Account").which.should.equal(
|
||||
"123456789012"
|
||||
)
|
||||
response["Summaries"][0].should.have.key("Account").which.should.equal(ACCOUNT_ID)
|
||||
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(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-1", "us-west-2"],
|
||||
)
|
||||
cf_conn.update_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-west-1", "us-west-2"],
|
||||
ParameterOverrides=param_overrides,
|
||||
)
|
||||
usw2_instance = cf_conn.describe_stack_instance(
|
||||
StackSetName="test_stack_set",
|
||||
StackInstanceAccount="123456789012",
|
||||
StackInstanceAccount=ACCOUNT_ID,
|
||||
StackInstanceRegion="us-west-2",
|
||||
)
|
||||
usw1_instance = cf_conn.describe_stack_instance(
|
||||
StackSetName="test_stack_set",
|
||||
StackInstanceAccount="123456789012",
|
||||
StackInstanceAccount=ACCOUNT_ID,
|
||||
StackInstanceRegion="us-west-1",
|
||||
)
|
||||
use1_instance = cf_conn.describe_stack_instance(
|
||||
StackSetName="test_stack_set",
|
||||
StackInstanceAccount="123456789012",
|
||||
StackInstanceAccount=ACCOUNT_ID,
|
||||
StackInstanceRegion="us-east-1",
|
||||
)
|
||||
|
||||
@ -383,13 +382,13 @@ def test_boto3_delete_stack_instances():
|
||||
)
|
||||
cf_conn.create_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-2"],
|
||||
)
|
||||
|
||||
cf_conn.delete_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1"],
|
||||
RetainStacks=False,
|
||||
)
|
||||
@ -410,7 +409,7 @@ def test_boto3_create_stack_instances():
|
||||
)
|
||||
cf_conn.create_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-2"],
|
||||
)
|
||||
|
||||
@ -419,7 +418,7 @@ def test_boto3_create_stack_instances():
|
||||
].should.have.length_of(2)
|
||||
cf_conn.list_stack_instances(StackSetName="test_stack_set")["Summaries"][0][
|
||||
"Account"
|
||||
].should.equal("123456789012")
|
||||
].should.equal(ACCOUNT_ID)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@ -440,13 +439,13 @@ def test_boto3_create_stack_instances_with_param_overrides():
|
||||
)
|
||||
cf_conn.create_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-2"],
|
||||
ParameterOverrides=param_overrides,
|
||||
)
|
||||
usw2_instance = cf_conn.describe_stack_instance(
|
||||
StackSetName="test_stack_set",
|
||||
StackInstanceAccount="123456789012",
|
||||
StackInstanceAccount=ACCOUNT_ID,
|
||||
StackInstanceRegion="us-west-2",
|
||||
)
|
||||
|
||||
@ -509,12 +508,12 @@ def test_boto3_list_stack_set_operations():
|
||||
)
|
||||
cf_conn.create_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-2"],
|
||||
)
|
||||
cf_conn.update_stack_instances(
|
||||
StackSetName="test_stack_set",
|
||||
Accounts=["123456789012"],
|
||||
Accounts=[ACCOUNT_ID],
|
||||
Regions=["us-east-1", "us-west-2"],
|
||||
)
|
||||
|
||||
@ -682,12 +681,12 @@ def test_create_stack_with_notification_arn():
|
||||
cf.create_stack(
|
||||
StackName="test_stack_with_notifications",
|
||||
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.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(
|
||||
StackName="test_stack_with_notifications",
|
||||
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.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
|
||||
@ -1019,7 +1018,7 @@ def test_describe_updated_stack():
|
||||
|
||||
cf_conn.update_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,
|
||||
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["StackName"].should.equal("test_stack")
|
||||
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"}])
|
||||
|
||||
|
||||
|
@ -43,6 +43,7 @@ from moto import (
|
||||
mock_sqs_deprecated,
|
||||
mock_elbv2,
|
||||
)
|
||||
from moto.core import ACCOUNT_ID
|
||||
from moto.dynamodb2.models import Table
|
||||
|
||||
from .fixtures import (
|
||||
@ -1912,7 +1913,7 @@ def test_stack_spot_fleet():
|
||||
"Type": "AWS::EC2::SpotFleet",
|
||||
"Properties": {
|
||||
"SpotFleetRequestConfigData": {
|
||||
"IamFleetRole": "arn:aws:iam::123456789012:role/fleet",
|
||||
"IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
|
||||
"SpotPrice": "0.12",
|
||||
"TargetCapacity": 6,
|
||||
"AllocationStrategy": "diversified",
|
||||
@ -1933,7 +1934,9 @@ def test_stack_spot_fleet():
|
||||
"SecurityGroups": [{"GroupId": "sg-123"}],
|
||||
"SubnetId": subnet_id,
|
||||
"IamInstanceProfile": {
|
||||
"Arn": "arn:aws:iam::123456789012:role/fleet"
|
||||
"Arn": "arn:aws:iam::{}:role/fleet".format(
|
||||
ACCOUNT_ID
|
||||
)
|
||||
},
|
||||
"WeightedCapacity": "4",
|
||||
"SpotPrice": "10.00",
|
||||
@ -1966,7 +1969,7 @@ def test_stack_spot_fleet():
|
||||
spot_fleet_config["SpotPrice"].should.equal("0.12")
|
||||
spot_fleet_config["TargetCapacity"].should.equal(6)
|
||||
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["FulfilledCapacity"].should.equal(6.0)
|
||||
@ -1999,7 +2002,7 @@ def test_stack_spot_fleet_should_figure_out_default_price():
|
||||
"Type": "AWS::EC2::SpotFleet",
|
||||
"Properties": {
|
||||
"SpotFleetRequestConfigData": {
|
||||
"IamFleetRole": "arn:aws:iam::123456789012:role/fleet",
|
||||
"IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
|
||||
"TargetCapacity": 6,
|
||||
"AllocationStrategy": "diversified",
|
||||
"LaunchSpecifications": [
|
||||
@ -2018,7 +2021,9 @@ def test_stack_spot_fleet_should_figure_out_default_price():
|
||||
"SecurityGroups": [{"GroupId": "sg-123"}],
|
||||
"SubnetId": subnet_id,
|
||||
"IamInstanceProfile": {
|
||||
"Arn": "arn:aws:iam::123456789012:role/fleet"
|
||||
"Arn": "arn:aws:iam::{}:role/fleet".format(
|
||||
ACCOUNT_ID
|
||||
)
|
||||
},
|
||||
"WeightedCapacity": "4",
|
||||
},
|
||||
|
222
tests/test_codecommit/test_codecommit.py
Normal file
222
tests/test_codecommit/test_codecommit.py
Normal 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. "
|
||||
)
|
720
tests/test_codepipeline/test_codepipeline.py
Normal file
720
tests/test_codepipeline/test_codepipeline.py
Normal 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"}],
|
||||
)
|
@ -6,6 +6,7 @@ from nose.tools import assert_raises
|
||||
|
||||
from moto import mock_cognitoidentity
|
||||
from moto.cognitoidentity.utils import get_random_identity_id
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
@mock_cognitoidentity
|
||||
@ -17,7 +18,9 @@ def test_create_identity_pool():
|
||||
AllowUnauthenticatedIdentities=False,
|
||||
SupportedLoginProviders={"graph.facebook.com": "123456789012345"},
|
||||
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=[
|
||||
{
|
||||
"ProviderName": "testprovider",
|
||||
@ -25,7 +28,7 @@ def test_create_identity_pool():
|
||||
"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"] != ""
|
||||
|
||||
@ -39,7 +42,9 @@ def test_describe_identity_pool():
|
||||
AllowUnauthenticatedIdentities=False,
|
||||
SupportedLoginProviders={"graph.facebook.com": "123456789012345"},
|
||||
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=[
|
||||
{
|
||||
"ProviderName": "testprovider",
|
||||
@ -47,7 +52,7 @@ def test_describe_identity_pool():
|
||||
"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"])
|
||||
|
@ -14,6 +14,7 @@ from jose import jws
|
||||
from nose.tools import assert_raises
|
||||
|
||||
from moto import mock_cognitoidp
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
@mock_cognitoidp
|
||||
@ -132,7 +133,9 @@ def test_create_user_pool_domain_custom_domain_config():
|
||||
|
||||
domain = str(uuid.uuid4())
|
||||
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"]
|
||||
result = conn.create_user_pool_domain(
|
||||
@ -177,7 +180,9 @@ def test_update_user_pool_domain():
|
||||
|
||||
domain = str(uuid.uuid4())
|
||||
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"]
|
||||
conn.create_user_pool_domain(UserPoolId=user_pool_id, Domain=domain)
|
||||
|
@ -7,6 +7,7 @@ from nose.tools import assert_raises
|
||||
|
||||
from moto import mock_s3
|
||||
from moto.config import mock_config
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
@mock_config
|
||||
@ -397,7 +398,9 @@ def test_put_configuration_aggregator():
|
||||
account_aggregation_source
|
||||
]
|
||||
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"]
|
||||
)
|
||||
assert (
|
||||
@ -626,10 +629,10 @@ def test_put_aggregation_authorization():
|
||||
Tags=[{"Key": "tag", "Value": "a"}],
|
||||
)
|
||||
|
||||
assert (
|
||||
result["AggregationAuthorization"]["AggregationAuthorizationArn"]
|
||||
== "arn:aws:config:us-west-2:123456789012:"
|
||||
"aggregation-authorization/012345678910/us-east-1"
|
||||
assert result["AggregationAuthorization"][
|
||||
"AggregationAuthorizationArn"
|
||||
] == "arn:aws:config:us-west-2:{}:aggregation-authorization/012345678910/us-east-1".format(
|
||||
ACCOUNT_ID
|
||||
)
|
||||
assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910"
|
||||
assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1"
|
||||
@ -641,10 +644,10 @@ def test_put_aggregation_authorization():
|
||||
result = client.put_aggregation_authorization(
|
||||
AuthorizedAccountId="012345678910", AuthorizedAwsRegion="us-east-1"
|
||||
)
|
||||
assert (
|
||||
result["AggregationAuthorization"]["AggregationAuthorizationArn"]
|
||||
== "arn:aws:config:us-west-2:123456789012:"
|
||||
"aggregation-authorization/012345678910/us-east-1"
|
||||
assert result["AggregationAuthorization"][
|
||||
"AggregationAuthorizationArn"
|
||||
] == "arn:aws:config:us-west-2:{}:aggregation-authorization/012345678910/us-east-1".format(
|
||||
ACCOUNT_ID
|
||||
)
|
||||
assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910"
|
||||
assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1"
|
||||
@ -1416,7 +1419,7 @@ def test_list_aggregate_discovered_resource():
|
||||
assert len(result["ResourceIdentifiers"]) == 12
|
||||
for x in range(0, 10):
|
||||
assert result["ResourceIdentifiers"][x] == {
|
||||
"SourceAccountId": "123456789012",
|
||||
"SourceAccountId": ACCOUNT_ID,
|
||||
"ResourceType": "AWS::S3::Bucket",
|
||||
"ResourceId": "bucket{}".format(x),
|
||||
"ResourceName": "bucket{}".format(x),
|
||||
@ -1424,7 +1427,7 @@ def test_list_aggregate_discovered_resource():
|
||||
}
|
||||
for x in range(11, 12):
|
||||
assert result["ResourceIdentifiers"][x] == {
|
||||
"SourceAccountId": "123456789012",
|
||||
"SourceAccountId": ACCOUNT_ID,
|
||||
"ResourceType": "AWS::S3::Bucket",
|
||||
"ResourceId": "eu-bucket{}".format(x),
|
||||
"ResourceName": "eu-bucket{}".format(x),
|
||||
|
@ -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.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
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user