Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Stephan Huber 2020-01-23 15:37:40 +01:00
commit 84210f6011
78 changed files with 1527 additions and 635 deletions

View File

@ -1240,14 +1240,14 @@
- [ ] create_commit - [ ] create_commit
- [ ] create_pull_request - [ ] create_pull_request
- [ ] create_pull_request_approval_rule - [ ] create_pull_request_approval_rule
- [ ] create_repository - [X] create_repository
- [ ] create_unreferenced_merge_commit - [ ] create_unreferenced_merge_commit
- [ ] delete_approval_rule_template - [ ] delete_approval_rule_template
- [ ] delete_branch - [ ] delete_branch
- [ ] delete_comment_content - [ ] delete_comment_content
- [ ] delete_file - [ ] delete_file
- [ ] delete_pull_request_approval_rule - [ ] delete_pull_request_approval_rule
- [ ] delete_repository - [X] delete_repository
- [ ] describe_merge_conflicts - [ ] describe_merge_conflicts
- [ ] describe_pull_request_events - [ ] describe_pull_request_events
- [ ] disassociate_approval_rule_template_from_repository - [ ] disassociate_approval_rule_template_from_repository
@ -1268,7 +1268,7 @@
- [ ] get_pull_request - [ ] get_pull_request
- [ ] get_pull_request_approval_states - [ ] get_pull_request_approval_states
- [ ] get_pull_request_override_state - [ ] get_pull_request_override_state
- [ ] get_repository - [X] get_repository
- [ ] get_repository_triggers - [ ] get_repository_triggers
- [ ] list_approval_rule_templates - [ ] list_approval_rule_templates
- [ ] list_associated_approval_rule_templates_for_repository - [ ] list_associated_approval_rule_templates_for_repository
@ -1374,7 +1374,7 @@
- [ ] update_profiling_group - [ ] update_profiling_group
## codepipeline ## codepipeline
13% implemented 22% implemented
- [ ] acknowledge_job - [ ] acknowledge_job
- [ ] acknowledge_third_party_job - [ ] acknowledge_third_party_job
- [ ] create_custom_action_type - [ ] create_custom_action_type
@ -1394,7 +1394,7 @@
- [ ] list_action_types - [ ] list_action_types
- [ ] list_pipeline_executions - [ ] list_pipeline_executions
- [X] list_pipelines - [X] list_pipelines
- [ ] list_tags_for_resource - [X] list_tags_for_resource
- [ ] list_webhooks - [ ] list_webhooks
- [ ] poll_for_jobs - [ ] poll_for_jobs
- [ ] poll_for_third_party_jobs - [ ] poll_for_third_party_jobs
@ -1408,8 +1408,8 @@
- [ ] register_webhook_with_third_party - [ ] register_webhook_with_third_party
- [ ] retry_stage_execution - [ ] retry_stage_execution
- [ ] start_pipeline_execution - [ ] start_pipeline_execution
- [ ] tag_resource - [X] tag_resource
- [ ] untag_resource - [X] untag_resource
- [X] update_pipeline - [X] update_pipeline
## codestar ## codestar
@ -2694,7 +2694,7 @@
## ec2-instance-connect ## ec2-instance-connect
0% implemented 0% implemented
- [ ] send_ssh_public_key - [x] send_ssh_public_key
## ecr ## ecr
27% implemented 27% implemented
@ -3763,7 +3763,7 @@
- [X] list_signing_certificates - [X] list_signing_certificates
- [ ] list_ssh_public_keys - [ ] list_ssh_public_keys
- [X] list_user_policies - [X] list_user_policies
- [ ] list_user_tags - [X] list_user_tags
- [X] list_users - [X] list_users
- [X] list_virtual_mfa_devices - [X] list_virtual_mfa_devices
- [X] put_group_policy - [X] put_group_policy

View File

@ -283,14 +283,14 @@ def test_describe_instances_allowed():
] ]
} }
access_key = ... access_key = ...
# create access key for an IAM user/assumed role that has the policy above. # create access key for an IAM user/assumed role that has the policy above.
# this part should call __exactly__ 4 AWS actions, so that authentication and authorization starts exactly after this # this part should call __exactly__ 4 AWS actions, so that authentication and authorization starts exactly after this
client = boto3.client('ec2', region_name='us-east-1', client = boto3.client('ec2', region_name='us-east-1',
aws_access_key_id=access_key['AccessKeyId'], aws_access_key_id=access_key['AccessKeyId'],
aws_secret_access_key=access_key['SecretAccessKey']) aws_secret_access_key=access_key['SecretAccessKey'])
# if the IAM principal whose access key is used, does not have the permission to describe instances, this will fail # if the IAM principal whose access key is used, does not have the permission to describe instances, this will fail
instances = client.describe_instances()['Reservations'][0]['Instances'] instances = client.describe_instances()['Reservations'][0]['Instances']
assert len(instances) == 0 assert len(instances) == 0
``` ```
@ -310,16 +310,16 @@ You need to ensure that the mocks are actually in place. Changes made to recent
have altered some of the mock behavior. In short, you need to ensure that you _always_ do the following: have altered some of the mock behavior. In short, you need to ensure that you _always_ do the following:
1. Ensure that your tests have dummy environment variables set up: 1. Ensure that your tests have dummy environment variables set up:
export AWS_ACCESS_KEY_ID='testing' export AWS_ACCESS_KEY_ID='testing'
export AWS_SECRET_ACCESS_KEY='testing' export AWS_SECRET_ACCESS_KEY='testing'
export AWS_SECURITY_TOKEN='testing' export AWS_SECURITY_TOKEN='testing'
export AWS_SESSION_TOKEN='testing' export AWS_SESSION_TOKEN='testing'
1. __VERY IMPORTANT__: ensure that you have your mocks set up __BEFORE__ your `boto3` client is established. 1. __VERY IMPORTANT__: ensure that you have your mocks set up __BEFORE__ your `boto3` client is established.
This can typically happen if you import a module that has a `boto3` client instantiated outside of a function. This can typically happen if you import a module that has a `boto3` client instantiated outside of a function.
See the pesky imports section below on how to work around this. See the pesky imports section below on how to work around this.
### Example on usage? ### Example on usage?
If you are a user of [pytest](https://pytest.org/en/latest/), you can leverage [pytest fixtures](https://pytest.org/en/latest/fixture.html#fixture) If you are a user of [pytest](https://pytest.org/en/latest/), you can leverage [pytest fixtures](https://pytest.org/en/latest/fixture.html#fixture)
to help set up your mocks and other AWS resources that you would need. to help set up your mocks and other AWS resources that you would need.
@ -354,7 +354,7 @@ def cloudwatch(aws_credentials):
... etc. ... etc.
``` ```
In the code sample above, all of the AWS/mocked fixtures take in a parameter of `aws_credentials`, In the code sample above, all of the AWS/mocked fixtures take in a parameter of `aws_credentials`,
which sets the proper fake environment variables. The fake environment variables are used so that `botocore` doesn't try to locate real which sets the proper fake environment variables. The fake environment variables are used so that `botocore` doesn't try to locate real
credentials on your system. credentials on your system.
@ -364,7 +364,7 @@ def test_create_bucket(s3):
# s3 is a fixture defined above that yields a boto3 s3 client. # s3 is a fixture defined above that yields a boto3 s3 client.
# Feel free to instantiate another boto3 S3 client -- Keep note of the region though. # Feel free to instantiate another boto3 S3 client -- Keep note of the region though.
s3.create_bucket(Bucket="somebucket") s3.create_bucket(Bucket="somebucket")
result = s3.list_buckets() result = s3.list_buckets()
assert len(result['Buckets']) == 1 assert len(result['Buckets']) == 1
assert result['Buckets'][0]['Name'] == 'somebucket' assert result['Buckets'][0]['Name'] == 'somebucket'
@ -373,7 +373,7 @@ def test_create_bucket(s3):
### What about those pesky imports? ### What about those pesky imports?
Recall earlier, it was mentioned that mocks should be established __BEFORE__ the clients are set up. One way Recall earlier, it was mentioned that mocks should be established __BEFORE__ the clients are set up. One way
to avoid import issues is to make use of local Python imports -- i.e. import the module inside of the unit to avoid import issues is to make use of local Python imports -- i.e. import the module inside of the unit
test you want to run vs. importing at the top of the file. test you want to run vs. importing at the top of the file.
Example: Example:
```python ```python
@ -381,12 +381,12 @@ def test_something(s3):
from some.package.that.does.something.with.s3 import some_func # <-- Local import for unit test from some.package.that.does.something.with.s3 import some_func # <-- Local import for unit test
# ^^ Importing here ensures that the mock has been established. # ^^ Importing here ensures that the mock has been established.
sume_func() # The mock has been established from the "s3" pytest fixture, so this function that uses some_func() # The mock has been established from the "s3" pytest fixture, so this function that uses
# a package-level S3 client will properly use the mock and not reach out to AWS. # a package-level S3 client will properly use the mock and not reach out to AWS.
``` ```
### Other caveats ### Other caveats
For Tox, Travis CI, and other build systems, you might need to also perform a `touch ~/.aws/credentials` For Tox, Travis CI, and other build systems, you might need to also perform a `touch ~/.aws/credentials`
command before running the tests. As long as that file is present (empty preferably) and the environment command before running the tests. As long as that file is present (empty preferably) and the environment
variables above are set, you should be good to go. variables above are set, you should be good to go.

View File

@ -76,7 +76,7 @@ Currently implemented Services:
+---------------------------+-----------------------+------------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Logs | @mock_logs | basic endpoints done | | Logs | @mock_logs | basic endpoints done |
+---------------------------+-----------------------+------------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Organizations | @mock_organizations | some core edpoints done | | Organizations | @mock_organizations | some core endpoints done |
+---------------------------+-----------------------+------------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Polly | @mock_polly | all endpoints done | | Polly | @mock_polly | all endpoints done |
+---------------------------+-----------------------+------------------------------------+ +---------------------------+-----------------------+------------------------------------+

View File

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

View File

@ -847,3 +847,11 @@ class APIGatewayBackend(BaseBackend):
apigateway_backends = {} apigateway_backends = {}
for region_name in Session().get_available_regions("apigateway"): for region_name in Session().get_available_regions("apigateway"):
apigateway_backends[region_name] = APIGatewayBackend(region_name) apigateway_backends[region_name] = APIGatewayBackend(region_name)
for region_name in Session().get_available_regions(
"apigateway", partition_name="aws-us-gov"
):
apigateway_backends[region_name] = APIGatewayBackend(region_name)
for region_name in Session().get_available_regions(
"apigateway", partition_name="aws-cn"
):
apigateway_backends[region_name] = APIGatewayBackend(region_name)

View File

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

View File

@ -23,7 +23,8 @@ import traceback
import weakref import weakref
import requests.adapters import requests.adapters
import boto.awslambda from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
from moto.iam.models import iam_backend from moto.iam.models import iam_backend
@ -52,9 +53,6 @@ try:
except ImportError: except ImportError:
from backports.tempfile import TemporaryDirectory from backports.tempfile import TemporaryDirectory
# The lambci container is returning a special escape character for the "RequestID" fields. Unicode 033:
# _stderr_regex = re.compile(r"START|END|REPORT RequestId: .*")
_stderr_regex = re.compile(r"\033\[\d+.*")
_orig_adapter_send = requests.adapters.HTTPAdapter.send _orig_adapter_send = requests.adapters.HTTPAdapter.send
docker_3 = docker.__version__[0] >= "3" docker_3 = docker.__version__[0] >= "3"
@ -384,7 +382,7 @@ class LambdaFunction(BaseModel):
try: try:
# TODO: I believe we can keep the container running and feed events as needed # TODO: I believe we can keep the container running and feed events as needed
# also need to hook it up to the other services so it can make kws/s3 etc calls # also need to hook it up to the other services so it can make kws/s3 etc calls
# Should get invoke_id /RequestId from invovation # Should get invoke_id /RequestId from invocation
env_vars = { env_vars = {
"AWS_LAMBDA_FUNCTION_TIMEOUT": self.timeout, "AWS_LAMBDA_FUNCTION_TIMEOUT": self.timeout,
"AWS_LAMBDA_FUNCTION_NAME": self.function_name, "AWS_LAMBDA_FUNCTION_NAME": self.function_name,
@ -452,14 +450,9 @@ class LambdaFunction(BaseModel):
if exit_code != 0: if exit_code != 0:
raise Exception("lambda invoke failed output: {}".format(output)) raise Exception("lambda invoke failed output: {}".format(output))
# strip out RequestId lines (TODO: This will return an additional '\n' in the response) # We only care about the response from the lambda
output = os.linesep.join( # Which is the last line of the output, according to https://github.com/lambci/docker-lambda/issues/25
[ output = output.splitlines()[-1]
line
for line in self.convert(output).splitlines()
if not _stderr_regex.match(line)
]
)
return output, False return output, False
except BaseException as e: except BaseException as e:
traceback.print_exc() traceback.print_exc()
@ -1043,10 +1036,10 @@ def do_validate_s3():
return os.environ.get("VALIDATE_LAMBDA_S3", "") in ["", "1", "true"] return os.environ.get("VALIDATE_LAMBDA_S3", "") in ["", "1", "true"]
# Handle us forgotten regions, unless Lambda truly only runs out of US and lambda_backends = {}
lambda_backends = { for region in Session().get_available_regions("lambda"):
_region.name: LambdaBackend(_region.name) for _region in boto.awslambda.regions() lambda_backends[region] = LambdaBackend(region)
} for region in Session().get_available_regions("lambda", partition_name="aws-us-gov"):
lambda_backends[region] = LambdaBackend(region)
lambda_backends["ap-southeast-2"] = LambdaBackend("ap-southeast-2") for region in Session().get_available_regions("lambda", partition_name="aws-cn"):
lambda_backends["us-gov-west-1"] = LambdaBackend("us-gov-west-1") lambda_backends[region] = LambdaBackend(region)

View File

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

View File

@ -1,5 +1,4 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import boto3
import re import re
import requests.adapters import requests.adapters
from itertools import cycle from itertools import cycle
@ -12,6 +11,8 @@ import docker
import functools import functools
import threading import threading
import dateutil.parser import dateutil.parser
from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.iam import iam_backends from moto.iam import iam_backends
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
@ -1317,7 +1318,10 @@ class BatchBackend(BaseBackend):
job.terminate(reason) job.terminate(reason)
available_regions = boto3.session.Session().get_available_regions("batch") batch_backends = {}
batch_backends = { for region in Session().get_available_regions("batch"):
region: BatchBackend(region_name=region) for region in available_regions batch_backends[region] = BatchBackend(region)
} for region in Session().get_available_regions("batch", partition_name="aws-us-gov"):
batch_backends[region] = BatchBackend(region)
for region in Session().get_available_regions("batch", partition_name="aws-cn"):
batch_backends[region] = BatchBackend(region)

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

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

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

View File

@ -26,3 +26,19 @@ class ResourceNotFoundException(JsonRESTError):
super(ResourceNotFoundException, self).__init__( super(ResourceNotFoundException, self).__init__(
"ResourceNotFoundException", message "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)
)

View File

@ -12,6 +12,8 @@ from moto.codepipeline.exceptions import (
InvalidStructureException, InvalidStructureException,
PipelineNotFoundException, PipelineNotFoundException,
ResourceNotFoundException, ResourceNotFoundException,
InvalidTagsException,
TooManyTagsException,
) )
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -54,6 +56,18 @@ class CodePipeline(BaseModel):
return pipeline 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): class CodePipelineBackend(BaseBackend):
def __init__(self): def __init__(self):
@ -93,10 +107,12 @@ class CodePipelineBackend(BaseBackend):
self.pipelines[pipeline["name"]] = CodePipeline(region, pipeline) self.pipelines[pipeline["name"]] = CodePipeline(region, pipeline)
if tags: if tags:
self.pipelines[pipeline["name"]].validate_tags(tags)
new_tags = {tag["key"]: tag["value"] for tag in tags} new_tags = {tag["key"]: tag["value"] for tag in tags}
self.pipelines[pipeline["name"]].tags.update(new_tags) self.pipelines[pipeline["name"]].tags.update(new_tags)
return pipeline, tags return pipeline, sorted(tags, key=lambda i: i["key"])
def get_pipeline(self, name): def get_pipeline(self, name):
codepipeline = self.pipelines.get(name) codepipeline = self.pipelines.get(name)
@ -145,7 +161,58 @@ class CodePipelineBackend(BaseBackend):
def delete_pipeline(self, name): def delete_pipeline(self, name):
self.pipelines.pop(name, None) 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 = {} codepipeline_backends = {}
for region in Session().get_available_regions("codepipeline"): for region in Session().get_available_regions("codepipeline"):
codepipeline_backends[region] = CodePipelineBackend() codepipeline_backends[region] = CodePipelineBackend()
for region in Session().get_available_regions(
"codepipeline", partition_name="aws-us-gov"
):
codepipeline_backends[region] = CodePipelineBackend()
for region in Session().get_available_regions("codepipeline", partition_name="aws-cn"):
codepipeline_backends[region] = CodePipelineBackend()

View File

@ -39,3 +39,24 @@ class CodePipelineResponse(BaseResponse):
self.codepipeline_backend.delete_pipeline(self._get_param("name")) self.codepipeline_backend.delete_pipeline(self._get_param("name"))
return "" return ""
def list_tags_for_resource(self):
tags = self.codepipeline_backend.list_tags_for_resource(
self._get_param("resourceArn")
)
return json.dumps({"tags": tags})
def tag_resource(self):
self.codepipeline_backend.tag_resource(
self._get_param("resourceArn"), self._get_param("tags")
)
return ""
def untag_resource(self):
self.codepipeline_backend.untag_resource(
self._get_param("resourceArn"), self._get_param("tagKeys")
)
return ""

View File

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

View File

@ -9,7 +9,7 @@ import os
import time import time
import uuid import uuid
import boto.cognito.identity from boto3 import Session
from jose import jws from jose import jws
from moto.compat import OrderedDict from moto.compat import OrderedDict
@ -108,7 +108,9 @@ class CognitoIdpUserPool(BaseModel):
return user_pool_json return user_pool_json
def create_jwt(self, client_id, username, expires_in=60 * 60, extra_data={}): def create_jwt(
self, client_id, username, token_use, expires_in=60 * 60, extra_data={}
):
now = int(time.time()) now = int(time.time())
payload = { payload = {
"iss": "https://cognito-idp.{}.amazonaws.com/{}".format( "iss": "https://cognito-idp.{}.amazonaws.com/{}".format(
@ -116,7 +118,7 @@ class CognitoIdpUserPool(BaseModel):
), ),
"sub": self.users[username].id, "sub": self.users[username].id,
"aud": client_id, "aud": client_id,
"token_use": "id", "token_use": token_use,
"auth_time": now, "auth_time": now,
"exp": now + expires_in, "exp": now + expires_in,
} }
@ -125,7 +127,10 @@ class CognitoIdpUserPool(BaseModel):
return jws.sign(payload, self.json_web_key, algorithm="RS256"), expires_in return jws.sign(payload, self.json_web_key, algorithm="RS256"), expires_in
def create_id_token(self, client_id, username): def create_id_token(self, client_id, username):
id_token, expires_in = self.create_jwt(client_id, username) extra_data = self.get_user_extra_data_by_client_id(client_id, username)
id_token, expires_in = self.create_jwt(
client_id, username, "id", extra_data=extra_data
)
self.id_tokens[id_token] = (client_id, username) self.id_tokens[id_token] = (client_id, username)
return id_token, expires_in return id_token, expires_in
@ -135,10 +140,7 @@ class CognitoIdpUserPool(BaseModel):
return refresh_token return refresh_token
def create_access_token(self, client_id, username): def create_access_token(self, client_id, username):
extra_data = self.get_user_extra_data_by_client_id(client_id, username) access_token, expires_in = self.create_jwt(client_id, username, "access")
access_token, expires_in = self.create_jwt(
client_id, username, extra_data=extra_data
)
self.access_tokens[access_token] = (client_id, username) self.access_tokens[access_token] = (client_id, username)
return access_token, expires_in return access_token, expires_in
@ -749,8 +751,14 @@ class CognitoIdpBackend(BaseBackend):
cognitoidp_backends = {} cognitoidp_backends = {}
for region in boto.cognito.identity.regions(): for region in Session().get_available_regions("cognito-idp"):
cognitoidp_backends[region.name] = CognitoIdpBackend(region.name) cognitoidp_backends[region] = CognitoIdpBackend(region)
for region in Session().get_available_regions(
"cognito-idp", partition_name="aws-us-gov"
):
cognitoidp_backends[region] = CognitoIdpBackend(region)
for region in Session().get_available_regions("cognito-idp", partition_name="aws-cn"):
cognitoidp_backends[region] = CognitoIdpBackend(region)
# Hack to help moto-server process requests on localhost, where the region isn't # Hack to help moto-server process requests on localhost, where the region isn't

View File

@ -1084,6 +1084,9 @@ class ConfigBackend(BaseBackend):
config_backends = {} config_backends = {}
boto3_session = Session() for region in Session().get_available_regions("config"):
for region in boto3_session.get_available_regions("config"): config_backends[region] = ConfigBackend()
for region in Session().get_available_regions("config", partition_name="aws-us-gov"):
config_backends[region] = ConfigBackend()
for region in Session().get_available_regions("config", partition_name="aws-cn"):
config_backends[region] = ConfigBackend() config_backends[region] = ConfigBackend()

View File

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

View File

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

View File

@ -977,10 +977,8 @@ class OpLessThan(Op):
lhs = self.lhs.expr(item) lhs = self.lhs.expr(item)
rhs = self.rhs.expr(item) rhs = self.rhs.expr(item)
# In python3 None is not a valid comparator when using < or > so must be handled specially # In python3 None is not a valid comparator when using < or > so must be handled specially
if lhs and rhs: if lhs is not None and rhs is not None:
return lhs < rhs return lhs < rhs
elif lhs is None and rhs:
return True
else: else:
return False return False
@ -992,10 +990,8 @@ class OpGreaterThan(Op):
lhs = self.lhs.expr(item) lhs = self.lhs.expr(item)
rhs = self.rhs.expr(item) rhs = self.rhs.expr(item)
# In python3 None is not a valid comparator when using < or > so must be handled specially # In python3 None is not a valid comparator when using < or > so must be handled specially
if lhs and rhs: if lhs is not None and rhs is not None:
return lhs > rhs return lhs > rhs
elif lhs and rhs is None:
return True
else: else:
return False return False
@ -1025,10 +1021,8 @@ class OpLessThanOrEqual(Op):
lhs = self.lhs.expr(item) lhs = self.lhs.expr(item)
rhs = self.rhs.expr(item) rhs = self.rhs.expr(item)
# In python3 None is not a valid comparator when using < or > so must be handled specially # In python3 None is not a valid comparator when using < or > so must be handled specially
if lhs and rhs: if lhs is not None and rhs is not None:
return lhs <= rhs return lhs <= rhs
elif lhs is None and rhs or lhs is None and rhs is None:
return True
else: else:
return False return False
@ -1040,10 +1034,8 @@ class OpGreaterThanOrEqual(Op):
lhs = self.lhs.expr(item) lhs = self.lhs.expr(item)
rhs = self.rhs.expr(item) rhs = self.rhs.expr(item)
# In python3 None is not a valid comparator when using < or > so must be handled specially # In python3 None is not a valid comparator when using < or > so must be handled specially
if lhs and rhs: if lhs is not None and rhs is not None:
return lhs >= rhs return lhs >= rhs
elif lhs and rhs is None or lhs is None and rhs is None:
return True
else: else:
return False return False

View File

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

View File

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

View File

@ -8,9 +8,9 @@ import os
import re import re
import six import six
import warnings import warnings
from pkg_resources import resource_filename
import boto.ec2 from boto3 import Session
from pkg_resources import resource_filename
from collections import defaultdict from collections import defaultdict
import weakref import weakref
@ -1473,7 +1473,13 @@ class Zone(object):
class RegionsAndZonesBackend(object): class RegionsAndZonesBackend(object):
regions = [Region(ri.name, ri.endpoint) for ri in boto.ec2.regions()] regions = []
for region in Session().get_available_regions("ec2"):
regions.append(Region(region, "ec2.{}.amazonaws.com".format(region)))
for region in Session().get_available_regions("ec2", partition_name="aws-us-gov"):
regions.append(Region(region, "ec2.{}.amazonaws.com".format(region)))
for region in Session().get_available_regions("ec2", partition_name="aws-cn"):
regions.append(Region(region, "ec2.{}.amazonaws.com.cn".format(region)))
zones = { zones = {
"ap-south-1": [ "ap-south-1": [
@ -1536,6 +1542,11 @@ class RegionsAndZonesBackend(object):
zone_id="apne1-az2", zone_id="apne1-az2",
), ),
], ],
"ap-east-1": [
Zone(region_name="ap-east-1", name="ap-east-1a", zone_id="ape1-az1"),
Zone(region_name="ap-east-1", name="ap-east-1b", zone_id="ape1-az2"),
Zone(region_name="ap-east-1", name="ap-east-1c", zone_id="ape1-az3"),
],
"sa-east-1": [ "sa-east-1": [
Zone(region_name="sa-east-1", name="sa-east-1a", zone_id="sae1-az1"), Zone(region_name="sa-east-1", name="sa-east-1a", zone_id="sae1-az1"),
Zone(region_name="sa-east-1", name="sa-east-1c", zone_id="sae1-az3"), Zone(region_name="sa-east-1", name="sa-east-1c", zone_id="sae1-az3"),
@ -1605,10 +1616,32 @@ class RegionsAndZonesBackend(object):
Zone(region_name="us-west-2", name="us-west-2b", zone_id="usw2-az1"), Zone(region_name="us-west-2", name="us-west-2b", zone_id="usw2-az1"),
Zone(region_name="us-west-2", name="us-west-2c", zone_id="usw2-az3"), Zone(region_name="us-west-2", name="us-west-2c", zone_id="usw2-az3"),
], ],
"me-south-1": [
Zone(region_name="me-south-1", name="me-south-1a", zone_id="mes1-az1"),
Zone(region_name="me-south-1", name="me-south-1b", zone_id="mes1-az2"),
Zone(region_name="me-south-1", name="me-south-1c", zone_id="mes1-az3"),
],
"cn-north-1": [ "cn-north-1": [
Zone(region_name="cn-north-1", name="cn-north-1a", zone_id="cnn1-az1"), Zone(region_name="cn-north-1", name="cn-north-1a", zone_id="cnn1-az1"),
Zone(region_name="cn-north-1", name="cn-north-1b", zone_id="cnn1-az2"), Zone(region_name="cn-north-1", name="cn-north-1b", zone_id="cnn1-az2"),
], ],
"cn-northwest-1": [
Zone(
region_name="cn-northwest-1",
name="cn-northwest-1a",
zone_id="cnnw1-az1",
),
Zone(
region_name="cn-northwest-1",
name="cn-northwest-1b",
zone_id="cnnw1-az2",
),
Zone(
region_name="cn-northwest-1",
name="cn-northwest-1c",
zone_id="cnnw1-az3",
),
],
"us-gov-west-1": [ "us-gov-west-1": [
Zone( Zone(
region_name="us-gov-west-1", name="us-gov-west-1a", zone_id="usgw1-az1" region_name="us-gov-west-1", name="us-gov-west-1a", zone_id="usgw1-az1"
@ -1620,6 +1653,17 @@ class RegionsAndZonesBackend(object):
region_name="us-gov-west-1", name="us-gov-west-1c", zone_id="usgw1-az3" region_name="us-gov-west-1", name="us-gov-west-1c", zone_id="usgw1-az3"
), ),
], ],
"us-gov-east-1": [
Zone(
region_name="us-gov-east-1", name="us-gov-east-1a", zone_id="usge1-az1"
),
Zone(
region_name="us-gov-east-1", name="us-gov-east-1b", zone_id="usge1-az2"
),
Zone(
region_name="us-gov-east-1", name="us-gov-east-1c", zone_id="usge1-az3"
),
],
} }
def describe_regions(self, region_names=[]): def describe_regions(self, region_names=[]):

View File

@ -104,7 +104,7 @@ class SecurityGroups(BaseResponse):
if self.is_not_dryrun("GrantSecurityGroupIngress"): if self.is_not_dryrun("GrantSecurityGroupIngress"):
for args in self._process_rules_from_querystring(): for args in self._process_rules_from_querystring():
self.ec2_backend.authorize_security_group_ingress(*args) self.ec2_backend.authorize_security_group_ingress(*args)
return AUTHORIZE_SECURITY_GROUP_INGRESS_REPONSE return AUTHORIZE_SECURITY_GROUP_INGRESS_RESPONSE
def create_security_group(self): def create_security_group(self):
name = self._get_param("GroupName") name = self._get_param("GroupName")
@ -158,7 +158,7 @@ class SecurityGroups(BaseResponse):
if self.is_not_dryrun("RevokeSecurityGroupIngress"): if self.is_not_dryrun("RevokeSecurityGroupIngress"):
for args in self._process_rules_from_querystring(): for args in self._process_rules_from_querystring():
self.ec2_backend.revoke_security_group_ingress(*args) self.ec2_backend.revoke_security_group_ingress(*args)
return REVOKE_SECURITY_GROUP_INGRESS_REPONSE return REVOKE_SECURITY_GROUP_INGRESS_RESPONSE
CREATE_SECURITY_GROUP_RESPONSE = """<CreateSecurityGroupResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> CREATE_SECURITY_GROUP_RESPONSE = """<CreateSecurityGroupResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
@ -265,12 +265,12 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = (
</DescribeSecurityGroupsResponse>""" </DescribeSecurityGroupsResponse>"""
) )
AUTHORIZE_SECURITY_GROUP_INGRESS_REPONSE = """<AuthorizeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> AUTHORIZE_SECURITY_GROUP_INGRESS_RESPONSE = """<AuthorizeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<return>true</return> <return>true</return>
</AuthorizeSecurityGroupIngressResponse>""" </AuthorizeSecurityGroupIngressResponse>"""
REVOKE_SECURITY_GROUP_INGRESS_REPONSE = """<RevokeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> REVOKE_SECURITY_GROUP_INGRESS_RESPONSE = """<RevokeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<return>true</return> <return>true</return>
</RevokeSecurityGroupIngressResponse>""" </RevokeSecurityGroupIngressResponse>"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,9 +3,10 @@ import re
import uuid import uuid
from datetime import datetime from datetime import datetime
from random import random, randint from random import random, randint
import boto3
import pytz import pytz
from boto3 import Session
from moto.core.exceptions import JsonRESTError from moto.core.exceptions import JsonRESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
@ -117,6 +118,7 @@ class TaskDefinition(BaseObject):
revision, revision,
container_definitions, container_definitions,
region_name, region_name,
network_mode=None,
volumes=None, volumes=None,
tags=None, tags=None,
): ):
@ -131,6 +133,10 @@ class TaskDefinition(BaseObject):
self.volumes = [] self.volumes = []
else: else:
self.volumes = volumes self.volumes = volumes
if network_mode is None:
self.network_mode = "bridge"
else:
self.network_mode = network_mode
@property @property
def response_object(self): def response_object(self):
@ -552,7 +558,7 @@ class EC2ContainerServiceBackend(BaseBackend):
raise Exception("{0} is not a cluster".format(cluster_name)) raise Exception("{0} is not a cluster".format(cluster_name))
def register_task_definition( def register_task_definition(
self, family, container_definitions, volumes, tags=None self, family, container_definitions, volumes=None, network_mode=None, tags=None
): ):
if family in self.task_definitions: if family in self.task_definitions:
last_id = self._get_last_task_definition_revision_id(family) last_id = self._get_last_task_definition_revision_id(family)
@ -561,7 +567,13 @@ class EC2ContainerServiceBackend(BaseBackend):
self.task_definitions[family] = {} self.task_definitions[family] = {}
revision = 1 revision = 1
task_definition = TaskDefinition( task_definition = TaskDefinition(
family, revision, container_definitions, self.region_name, volumes, tags family,
revision,
container_definitions,
self.region_name,
volumes=volumes,
network_mode=network_mode,
tags=tags,
) )
self.task_definitions[family][revision] = task_definition self.task_definitions[family][revision] = task_definition
@ -1302,7 +1314,10 @@ class EC2ContainerServiceBackend(BaseBackend):
raise NotImplementedError() raise NotImplementedError()
available_regions = boto3.session.Session().get_available_regions("ecs") ecs_backends = {}
ecs_backends = { for region in Session().get_available_regions("ecs"):
region: EC2ContainerServiceBackend(region) for region in available_regions ecs_backends[region] = EC2ContainerServiceBackend(region)
} for region in Session().get_available_regions("ecs", partition_name="aws-us-gov"):
ecs_backends[region] = EC2ContainerServiceBackend(region)
for region in Session().get_available_regions("ecs", partition_name="aws-cn"):
ecs_backends[region] = EC2ContainerServiceBackend(region)

View File

@ -62,8 +62,13 @@ class EC2ContainerServiceResponse(BaseResponse):
container_definitions = self._get_param("containerDefinitions") container_definitions = self._get_param("containerDefinitions")
volumes = self._get_param("volumes") volumes = self._get_param("volumes")
tags = self._get_param("tags") tags = self._get_param("tags")
network_mode = self._get_param("networkMode")
task_definition = self.ecs_backend.register_task_definition( task_definition = self.ecs_backend.register_task_definition(
family, container_definitions, volumes, tags family,
container_definitions,
volumes=volumes,
network_mode=network_mode,
tags=tags,
) )
return json.dumps({"taskDefinition": task_definition.response_object}) return json.dumps({"taskDefinition": task_definition.response_object})

View File

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

View File

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

View File

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

View File

@ -543,7 +543,7 @@ class Group(BaseModel):
class User(BaseModel): class User(BaseModel):
def __init__(self, name, path=None): def __init__(self, name, path=None, tags=None):
self.name = name self.name = name
self.id = random_resource_id() self.id = random_resource_id()
self.path = path if path else "/" self.path = path if path else "/"
@ -556,6 +556,7 @@ class User(BaseModel):
self.password = None self.password = None
self.password_reset_required = False self.password_reset_required = False
self.signing_certificates = {} self.signing_certificates = {}
self.tags = tags
@property @property
def arn(self): def arn(self):
@ -1421,13 +1422,13 @@ class IAMBackend(BaseBackend):
"The group with name {0} cannot be found.".format(group_name) "The group with name {0} cannot be found.".format(group_name)
) )
def create_user(self, user_name, path="/"): def create_user(self, user_name, path="/", tags=None):
if user_name in self.users: if user_name in self.users:
raise IAMConflictException( raise IAMConflictException(
"EntityAlreadyExists", "User {0} already exists".format(user_name) "EntityAlreadyExists", "User {0} already exists".format(user_name)
) )
user = User(user_name, path) user = User(user_name, path, tags)
self.users[user_name] = user self.users[user_name] = user
return user return user
@ -1583,6 +1584,10 @@ class IAMBackend(BaseBackend):
user = self.get_user(user_name) user = self.get_user(user_name)
return user.policies.keys() return user.policies.keys()
def list_user_tags(self, user_name):
user = self.get_user(user_name)
return user.tags
def put_user_policy(self, user_name, policy_name, policy_json): def put_user_policy(self, user_name, policy_name, policy_json):
user = self.get_user(user_name) user = self.get_user(user_name)

View File

@ -440,8 +440,8 @@ class IamResponse(BaseResponse):
def create_user(self): def create_user(self):
user_name = self._get_param("UserName") user_name = self._get_param("UserName")
path = self._get_param("Path") path = self._get_param("Path")
tags = self._get_multi_param("Tags.member")
user = iam_backend.create_user(user_name, path) user = iam_backend.create_user(user_name, path, tags)
template = self.response_template(USER_TEMPLATE) template = self.response_template(USER_TEMPLATE)
return template.render(action="Create", user=user) return template.render(action="Create", user=user)
@ -538,6 +538,12 @@ class IamResponse(BaseResponse):
template = self.response_template(LIST_USER_POLICIES_TEMPLATE) template = self.response_template(LIST_USER_POLICIES_TEMPLATE)
return template.render(policies=policies) return template.render(policies=policies)
def list_user_tags(self):
user_name = self._get_param("UserName")
tags = iam_backend.list_user_tags(user_name)
template = self.response_template(LIST_USER_TAGS_TEMPLATE)
return template.render(user_tags=tags or [])
def put_user_policy(self): def put_user_policy(self):
user_name = self._get_param("UserName") user_name = self._get_param("UserName")
policy_name = self._get_param("PolicyName") policy_name = self._get_param("PolicyName")
@ -1699,6 +1705,23 @@ LIST_USER_POLICIES_TEMPLATE = """<ListUserPoliciesResponse>
</ResponseMetadata> </ResponseMetadata>
</ListUserPoliciesResponse>""" </ListUserPoliciesResponse>"""
LIST_USER_TAGS_TEMPLATE = """<ListUserTagsResponse>
<ListUserTagsResult>
<Tags>
{% for tag in user_tags %}
<item>
<Key>{{ tag.Key }}</Key>
<Value>{{ tag.Value }}</Value>
</item>
{% endfor %}
</Tags>
<IsTruncated>false</IsTruncated>
</ListUserTagsResult>
<ResponseMetadata>
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ResponseMetadata>
</ListUserTagsResponse>"""
CREATE_ACCESS_KEY_TEMPLATE = """<CreateAccessKeyResponse> CREATE_ACCESS_KEY_TEMPLATE = """<CreateAccessKeyResponse>
<CreateAccessKeyResult> <CreateAccessKeyResult>
<AccessKey> <AccessKey>

View File

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

View File

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

View File

@ -2,7 +2,6 @@ from __future__ import unicode_literals
import datetime import datetime
import time import time
import boto.kinesis
import re import re
import six import six
import itertools import itertools
@ -10,6 +9,8 @@ import itertools
from operator import attrgetter from operator import attrgetter
from hashlib import md5 from hashlib import md5
from boto3 import Session
from moto.compat import OrderedDict from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
@ -530,5 +531,9 @@ class KinesisBackend(BaseBackend):
kinesis_backends = {} kinesis_backends = {}
for region in boto.kinesis.regions(): for region in Session().get_available_regions("kinesis"):
kinesis_backends[region.name] = KinesisBackend() kinesis_backends[region] = KinesisBackend()
for region in Session().get_available_regions("kinesis", partition_name="aws-us-gov"):
kinesis_backends[region] = KinesisBackend()
for region in Session().get_available_regions("kinesis", partition_name="aws-cn"):
kinesis_backends[region] = KinesisBackend()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from builtins import str from builtins import str
import boto3
import json import json
import re import re
from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core import ACCOUNT_ID from moto.core import ACCOUNT_ID
from .exceptions import BadRequestException from .exceptions import BadRequestException
@ -350,7 +351,14 @@ class ResourceGroupsBackend(BaseBackend):
return self.groups.by_name[group_name] return self.groups.by_name[group_name]
available_regions = boto3.session.Session().get_available_regions("resource-groups") resourcegroups_backends = {}
resourcegroups_backends = { for region in Session().get_available_regions("resource-groups"):
region: ResourceGroupsBackend(region_name=region) for region in available_regions resourcegroups_backends[region] = ResourceGroupsBackend(region)
} for region in Session().get_available_regions(
"resource-groups", partition_name="aws-us-gov"
):
resourcegroups_backends[region] = ResourceGroupsBackend(region)
for region in Session().get_available_regions(
"resource-groups", partition_name="aws-cn"
):
resourcegroups_backends[region] = ResourceGroupsBackend(region)

View File

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

View File

@ -271,6 +271,7 @@ LIST_RRSET_RESPONSE = """<ListResourceRecordSetsResponse xmlns="https://route53.
{{ record_set.to_xml() }} {{ record_set.to_xml() }}
{% endfor %} {% endfor %}
</ResourceRecordSets> </ResourceRecordSets>
<IsTruncated>false</IsTruncated>
</ListResourceRecordSetsResponse>""" </ListResourceRecordSetsResponse>"""
CHANGE_RRSET_RESPONSE = """<ChangeResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/"> CHANGE_RRSET_RESPONSE = """<ChangeResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">

View File

@ -1482,7 +1482,7 @@ S3_ALL_BUCKETS = """<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2
{% for bucket in buckets %} {% for bucket in buckets %}
<Bucket> <Bucket>
<Name>{{ bucket.name }}</Name> <Name>{{ bucket.name }}</Name>
<CreationDate>{{ bucket.creation_date }}</CreationDate> <CreationDate>{{ bucket.creation_date.isoformat() }}</CreationDate>
</Bucket> </Bucket>
{% endfor %} {% endfor %}
</Buckets> </Buckets>
@ -1491,7 +1491,9 @@ S3_ALL_BUCKETS = """<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2
S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?> S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>{{ bucket.name }}</Name> <Name>{{ bucket.name }}</Name>
{% if prefix != None %}
<Prefix>{{ prefix }}</Prefix> <Prefix>{{ prefix }}</Prefix>
{% endif %}
<MaxKeys>{{ max_keys }}</MaxKeys> <MaxKeys>{{ max_keys }}</MaxKeys>
<Delimiter>{{ delimiter }}</Delimiter> <Delimiter>{{ delimiter }}</Delimiter>
<IsTruncated>{{ is_truncated }}</IsTruncated> <IsTruncated>{{ is_truncated }}</IsTruncated>
@ -1523,7 +1525,9 @@ S3_BUCKET_GET_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
S3_BUCKET_GET_RESPONSE_V2 = """<?xml version="1.0" encoding="UTF-8"?> S3_BUCKET_GET_RESPONSE_V2 = """<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>{{ bucket.name }}</Name> <Name>{{ bucket.name }}</Name>
{% if prefix != None %}
<Prefix>{{ prefix }}</Prefix> <Prefix>{{ prefix }}</Prefix>
{% endif %}
<MaxKeys>{{ max_keys }}</MaxKeys> <MaxKeys>{{ max_keys }}</MaxKeys>
<KeyCount>{{ key_count }}</KeyCount> <KeyCount>{{ key_count }}</KeyCount>
{% if delimiter %} {% if delimiter %}
@ -1684,7 +1688,9 @@ S3_BUCKET_GET_VERSIONING = """<?xml version="1.0" encoding="UTF-8"?>
S3_BUCKET_GET_VERSIONS = """<?xml version="1.0" encoding="UTF-8"?> S3_BUCKET_GET_VERSIONS = """<?xml version="1.0" encoding="UTF-8"?>
<ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01"> <ListVersionsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
<Name>{{ bucket.name }}</Name> <Name>{{ bucket.name }}</Name>
{% if prefix != None %}
<Prefix>{{ prefix }}</Prefix> <Prefix>{{ prefix }}</Prefix>
{% endif %}
<KeyMarker>{{ key_marker }}</KeyMarker> <KeyMarker>{{ key_marker }}</KeyMarker>
<MaxKeys>{{ max_keys }}</MaxKeys> <MaxKeys>{{ max_keys }}</MaxKeys>
<IsTruncated>{{ is_truncated }}</IsTruncated> <IsTruncated>{{ is_truncated }}</IsTruncated>
@ -1863,7 +1869,6 @@ S3_MULTIPART_LIST_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
<ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID> <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
<DisplayName>webfile</DisplayName> <DisplayName>webfile</DisplayName>
</Owner> </Owner>
<StorageClass>STANDARD</StorageClass>
<PartNumberMarker>1</PartNumberMarker> <PartNumberMarker>1</PartNumberMarker>
<NextPartNumberMarker>{{ count }}</NextPartNumberMarker> <NextPartNumberMarker>{{ count }}</NextPartNumberMarker>
<MaxParts>{{ count }}</MaxParts> <MaxParts>{{ count }}</MaxParts>

View File

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

View File

@ -397,10 +397,6 @@ class SNSBackend(BaseBackend):
return self._get_values_nexttoken(self.topics, next_token) return self._get_values_nexttoken(self.topics, next_token)
def delete_topic(self, arn): def delete_topic(self, arn):
topic = self.get_topic(arn)
subscriptions = self._get_topic_subscriptions(topic)
for sub in subscriptions:
self.unsubscribe(sub.arn)
self.topics.pop(arn) self.topics.pop(arn)
def get_topic(self, arn): def get_topic(self, arn):
@ -466,7 +462,7 @@ class SNSBackend(BaseBackend):
return None return None
def unsubscribe(self, subscription_arn): def unsubscribe(self, subscription_arn):
self.subscriptions.pop(subscription_arn) self.subscriptions.pop(subscription_arn, None)
def list_subscriptions(self, topic_arn=None, next_token=None): def list_subscriptions(self, topic_arn=None, next_token=None):
if topic_arn: if topic_arn:
@ -707,6 +703,10 @@ class SNSBackend(BaseBackend):
sns_backends = {} sns_backends = {}
for region in Session().get_available_regions("sns"): for region in Session().get_available_regions("sns"):
sns_backends[region] = SNSBackend(region) sns_backends[region] = SNSBackend(region)
for region in Session().get_available_regions("sns", partition_name="aws-us-gov"):
sns_backends[region] = SNSBackend(region)
for region in Session().get_available_regions("sns", partition_name="aws-cn"):
sns_backends[region] = SNSBackend(region)
DEFAULT_EFFECTIVE_DELIVERY_POLICY = { DEFAULT_EFFECTIVE_DELIVERY_POLICY = {

View File

@ -8,7 +8,7 @@ import six
import struct import struct
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
import boto.sqs from boto3 import Session
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -183,6 +183,7 @@ class Queue(BaseModel):
"MaximumMessageSize", "MaximumMessageSize",
"MessageRetentionPeriod", "MessageRetentionPeriod",
"QueueArn", "QueueArn",
"RedrivePolicy",
"ReceiveMessageWaitTimeSeconds", "ReceiveMessageWaitTimeSeconds",
"VisibilityTimeout", "VisibilityTimeout",
] ]
@ -857,5 +858,9 @@ class SQSBackend(BaseBackend):
sqs_backends = {} sqs_backends = {}
for region in boto.sqs.regions(): for region in Session().get_available_regions("sqs"):
sqs_backends[region.name] = SQSBackend(region.name) sqs_backends[region] = SQSBackend(region)
for region in Session().get_available_regions("sqs", partition_name="aws-us-gov"):
sqs_backends[region] = SQSBackend(region)
for region in Session().get_available_regions("sqs", partition_name="aws-cn"):
sqs_backends[region] = SQSBackend(region)

View File

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

View File

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

View File

@ -20,8 +20,8 @@ import jinja2
from prompt_toolkit import ( from prompt_toolkit import (
prompt prompt
) )
from prompt_toolkit.contrib.completers import WordCompleter from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.shortcuts import print_tokens from prompt_toolkit.shortcuts import print_formatted_text
from botocore import xform_name from botocore import xform_name
from botocore.session import Session from botocore.session import Session
@ -149,12 +149,12 @@ def append_mock_dict_to_backends_py(service):
with open(path) as f: with open(path) as f:
lines = [_.replace('\n', '') for _ in f.readlines()] lines = [_.replace('\n', '') for _ in f.readlines()]
if any(_ for _ in lines if re.match(".*'{}': {}_backends.*".format(service, service), _)): if any(_ for _ in lines if re.match(".*\"{}\": {}_backends.*".format(service, service), _)):
return return
filtered_lines = [_ for _ in lines if re.match(".*'.*':.*_backends.*", _)] filtered_lines = [_ for _ in lines if re.match(".*\".*\":.*_backends.*", _)]
last_elem_line_index = lines.index(filtered_lines[-1]) last_elem_line_index = lines.index(filtered_lines[-1])
new_line = " '{}': {}_backends,".format(service, get_escaped_service(service)) new_line = " \"{}\": {}_backends,".format(service, get_escaped_service(service))
prev_line = lines[last_elem_line_index] prev_line = lines[last_elem_line_index]
if not prev_line.endswith('{') and not prev_line.endswith(','): if not prev_line.endswith('{') and not prev_line.endswith(','):
lines[last_elem_line_index] += ',' lines[last_elem_line_index] += ','

View File

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

View File

@ -43,7 +43,7 @@ install_requires = [
"python-jose<4.0.0", "python-jose<4.0.0",
"mock", "mock",
"docker>=2.5.1", "docker>=2.5.1",
"jsondiff==1.1.2", "jsondiff>=1.1.2",
"aws-xray-sdk!=0.96,>=0.93", "aws-xray-sdk!=0.96,>=0.93",
"responses>=0.9.0", "responses>=0.9.0",
"idna<2.9,>=2.5", "idna<2.9,>=2.5",

View File

@ -706,14 +706,14 @@ def test_create_autoscaling_group_boto3():
"ResourceId": "test_asg", "ResourceId": "test_asg",
"ResourceType": "auto-scaling-group", "ResourceType": "auto-scaling-group",
"Key": "propogated-tag-key", "Key": "propogated-tag-key",
"Value": "propogate-tag-value", "Value": "propagate-tag-value",
"PropagateAtLaunch": True, "PropagateAtLaunch": True,
}, },
{ {
"ResourceId": "test_asg", "ResourceId": "test_asg",
"ResourceType": "auto-scaling-group", "ResourceType": "auto-scaling-group",
"Key": "not-propogated-tag-key", "Key": "not-propogated-tag-key",
"Value": "not-propogate-tag-value", "Value": "not-propagate-tag-value",
"PropagateAtLaunch": False, "PropagateAtLaunch": False,
}, },
], ],
@ -744,14 +744,14 @@ def test_create_autoscaling_group_from_instance():
"ResourceId": "test_asg", "ResourceId": "test_asg",
"ResourceType": "auto-scaling-group", "ResourceType": "auto-scaling-group",
"Key": "propogated-tag-key", "Key": "propogated-tag-key",
"Value": "propogate-tag-value", "Value": "propagate-tag-value",
"PropagateAtLaunch": True, "PropagateAtLaunch": True,
}, },
{ {
"ResourceId": "test_asg", "ResourceId": "test_asg",
"ResourceType": "auto-scaling-group", "ResourceType": "auto-scaling-group",
"Key": "not-propogated-tag-key", "Key": "not-propogated-tag-key",
"Value": "not-propogate-tag-value", "Value": "not-propagate-tag-value",
"PropagateAtLaunch": False, "PropagateAtLaunch": False,
}, },
], ],
@ -1062,7 +1062,7 @@ def test_detach_one_instance_decrement():
"ResourceId": "test_asg", "ResourceId": "test_asg",
"ResourceType": "auto-scaling-group", "ResourceType": "auto-scaling-group",
"Key": "propogated-tag-key", "Key": "propogated-tag-key",
"Value": "propogate-tag-value", "Value": "propagate-tag-value",
"PropagateAtLaunch": True, "PropagateAtLaunch": True,
} }
], ],
@ -1116,7 +1116,7 @@ def test_detach_one_instance():
"ResourceId": "test_asg", "ResourceId": "test_asg",
"ResourceType": "auto-scaling-group", "ResourceType": "auto-scaling-group",
"Key": "propogated-tag-key", "Key": "propogated-tag-key",
"Value": "propogate-tag-value", "Value": "propagate-tag-value",
"PropagateAtLaunch": True, "PropagateAtLaunch": True,
} }
], ],
@ -1169,7 +1169,7 @@ def test_attach_one_instance():
"ResourceId": "test_asg", "ResourceId": "test_asg",
"ResourceType": "auto-scaling-group", "ResourceType": "auto-scaling-group",
"Key": "propogated-tag-key", "Key": "propogated-tag-key",
"Value": "propogate-tag-value", "Value": "propagate-tag-value",
"PropagateAtLaunch": True, "PropagateAtLaunch": True,
} }
], ],

View File

@ -58,8 +58,7 @@ def lambda_handler(event, context):
volume_id = event.get('volume_id') volume_id = event.get('volume_id')
vol = ec2.Volume(volume_id) vol = ec2.Volume(volume_id)
print('get volume details for %s\\nVolume - %s state=%s, size=%s' % (volume_id, volume_id, vol.state, vol.size)) return {{'id': vol.id, 'state': vol.state, 'size': vol.size}}
return event
""".format( """.format(
base_url="motoserver:5000" base_url="motoserver:5000"
if settings.TEST_SERVER_MODE if settings.TEST_SERVER_MODE
@ -181,27 +180,9 @@ if settings.TEST_SERVER_MODE:
Payload=json.dumps(in_data), Payload=json.dumps(in_data),
) )
result["StatusCode"].should.equal(202) result["StatusCode"].should.equal(202)
msg = "get volume details for %s\nVolume - %s state=%s, size=%s\n%s" % ( actual_payload = json.loads(result["Payload"].read().decode("utf-8"))
vol.id, expected_payload = {"id": vol.id, "state": vol.state, "size": vol.size}
vol.id, actual_payload.should.equal(expected_payload)
vol.state,
vol.size,
json.dumps(in_data).replace(
" ", ""
), # Makes the tests pass as the result is missing the whitespace
)
log_result = base64.b64decode(result["LogResult"]).decode("utf-8")
# The Docker lambda invocation will return an additional '\n', so need to replace it:
log_result = log_result.replace("\n\n", "\n")
log_result.should.equal(msg)
payload = result["Payload"].read().decode("utf-8")
# The Docker lambda invocation will return an additional '\n', so need to replace it:
payload = payload.replace("\n\n", "\n")
payload.should.equal(msg)
@mock_logs @mock_logs

View File

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

View File

@ -13,52 +13,7 @@ from moto import mock_codepipeline, mock_iam
def test_create_pipeline(): def test_create_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1") client = boto3.client("codepipeline", region_name="us-east-1")
response = client.create_pipeline( response = create_basic_codepipeline(client, "test-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": "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"}],
)
response["pipeline"].should.equal( response["pipeline"].should.equal(
{ {
@ -120,98 +75,10 @@ def test_create_pipeline():
def test_create_pipeline_errors(): def test_create_pipeline_errors():
client = boto3.client("codepipeline", region_name="us-east-1") client = boto3.client("codepipeline", region_name="us-east-1")
client_iam = boto3.client("iam", region_name="us-east-1") client_iam = boto3.client("iam", region_name="us-east-1")
client.create_pipeline( create_basic_codepipeline(client, "test-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": "test-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"},],
},
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
},
],
},
],
}
)
with assert_raises(ClientError) as e: with assert_raises(ClientError) as e:
client.create_pipeline( create_basic_codepipeline(client, "test-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": "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 = e.exception
ex.operation_name.should.equal("CreatePipeline") ex.operation_name.should.equal("CreatePipeline")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
@ -348,52 +215,7 @@ def test_create_pipeline_errors():
@mock_codepipeline @mock_codepipeline
def test_get_pipeline(): def test_get_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1") client = boto3.client("codepipeline", region_name="us-east-1")
client.create_pipeline( create_basic_codepipeline(client, "test-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": "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"}],
)
response = client.get_pipeline(name="test-pipeline") response = client.get_pipeline(name="test-pipeline")
@ -474,53 +296,7 @@ def test_get_pipeline_errors():
@mock_codepipeline @mock_codepipeline
def test_update_pipeline(): def test_update_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1") client = boto3.client("codepipeline", region_name="us-east-1")
role_arn = get_role_arn() create_basic_codepipeline(client, "test-pipeline")
client.create_pipeline(
pipeline={
"name": "test-pipeline",
"roleArn": 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"}],
)
response = client.get_pipeline(name="test-pipeline") response = client.get_pipeline(name="test-pipeline")
created_time = response["metadata"]["created"] created_time = response["metadata"]["created"]
@ -529,7 +305,7 @@ def test_update_pipeline():
response = client.update_pipeline( response = client.update_pipeline(
pipeline={ pipeline={
"name": "test-pipeline", "name": "test-pipeline",
"roleArn": role_arn, "roleArn": get_role_arn(),
"artifactStore": { "artifactStore": {
"type": "S3", "type": "S3",
"location": "codepipeline-us-east-1-123456789012", "location": "codepipeline-us-east-1-123456789012",
@ -692,105 +468,19 @@ def test_update_pipeline_errors():
@mock_codepipeline @mock_codepipeline
def test_list_pipelines(): def test_list_pipelines():
client = boto3.client("codepipeline", region_name="us-east-1") client = boto3.client("codepipeline", region_name="us-east-1")
client.create_pipeline( name_1 = "test-pipeline-1"
pipeline={ create_basic_codepipeline(client, name_1)
"name": "test-pipeline-1", name_2 = "test-pipeline-2"
"roleArn": get_role_arn(), create_basic_codepipeline(client, name_2)
"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",
},
},
],
},
],
},
)
client.create_pipeline(
pipeline={
"name": "test-pipeline-2",
"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",
},
},
],
},
],
},
)
response = client.list_pipelines() response = client.list_pipelines()
response["pipelines"].should.have.length_of(2) response["pipelines"].should.have.length_of(2)
response["pipelines"][0]["name"].should.equal("test-pipeline-1") response["pipelines"][0]["name"].should.equal(name_1)
response["pipelines"][0]["version"].should.equal(1) response["pipelines"][0]["version"].should.equal(1)
response["pipelines"][0]["created"].should.be.a(datetime) response["pipelines"][0]["created"].should.be.a(datetime)
response["pipelines"][0]["updated"].should.be.a(datetime) response["pipelines"][0]["updated"].should.be.a(datetime)
response["pipelines"][1]["name"].should.equal("test-pipeline-2") response["pipelines"][1]["name"].should.equal(name_2)
response["pipelines"][1]["version"].should.equal(1) response["pipelines"][1]["version"].should.equal(1)
response["pipelines"][1]["created"].should.be.a(datetime) response["pipelines"][1]["created"].should.be.a(datetime)
response["pipelines"][1]["updated"].should.be.a(datetime) response["pipelines"][1]["updated"].should.be.a(datetime)
@ -799,68 +489,172 @@ def test_list_pipelines():
@mock_codepipeline @mock_codepipeline
def test_delete_pipeline(): def test_delete_pipeline():
client = boto3.client("codepipeline", region_name="us-east-1") client = boto3.client("codepipeline", region_name="us-east-1")
client.create_pipeline( name = "test-pipeline"
pipeline={ create_basic_codepipeline(client, name)
"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": "test-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"},],
},
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
},
],
},
],
},
)
client.list_pipelines()["pipelines"].should.have.length_of(1) client.list_pipelines()["pipelines"].should.have.length_of(1)
client.delete_pipeline(name="test-pipeline") client.delete_pipeline(name=name)
client.list_pipelines()["pipelines"].should.have.length_of(0) client.list_pipelines()["pipelines"].should.have.length_of(0)
# deleting a not existing pipeline, should raise no exception # deleting a not existing pipeline, should raise no exception
client.delete_pipeline(name="test-pipeline") 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 @mock_iam
def get_role_arn(): def get_role_arn():
iam = boto3.client("iam", region_name="us-east-1") client = boto3.client("iam", region_name="us-east-1")
try: try:
return iam.get_role(RoleName="test-role")["Role"]["Arn"] return client.get_role(RoleName="test-role")["Role"]["Arn"]
except ClientError: except ClientError:
return iam.create_role( return client.create_role(
RoleName="test-role", RoleName="test-role",
AssumeRolePolicyDocument=json.dumps( AssumeRolePolicyDocument=json.dumps(
{ {
@ -875,3 +669,52 @@ def get_role_arn():
} }
), ),
)["Role"]["Arn"] )["Role"]["Arn"]
def create_basic_codepipeline(client, name):
return client.create_pipeline(
pipeline={
"name": name,
"roleArn": get_role_arn(),
"artifactStore": {
"type": "S3",
"location": "codepipeline-us-east-1-123456789012",
},
"stages": [
{
"name": "Stage-1",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Source",
"owner": "AWS",
"provider": "S3",
"version": "1",
},
"configuration": {
"S3Bucket": "test-bucket",
"S3ObjectKey": "test-object",
},
"outputArtifacts": [{"name": "artifact"},],
},
],
},
{
"name": "Stage-2",
"actions": [
{
"name": "Action-1",
"actionTypeId": {
"category": "Approval",
"owner": "AWS",
"provider": "Manual",
"version": "1",
},
},
],
},
],
},
tags=[{"key": "key", "value": "value"}],
)

View File

@ -1142,11 +1142,13 @@ def test_token_legitimacy():
id_claims = json.loads(jws.verify(id_token, json_web_key, "RS256")) id_claims = json.loads(jws.verify(id_token, json_web_key, "RS256"))
id_claims["iss"].should.equal(issuer) id_claims["iss"].should.equal(issuer)
id_claims["aud"].should.equal(client_id) id_claims["aud"].should.equal(client_id)
id_claims["token_use"].should.equal("id")
for k, v in outputs["additional_fields"].items():
id_claims[k].should.equal(v)
access_claims = json.loads(jws.verify(access_token, json_web_key, "RS256")) access_claims = json.loads(jws.verify(access_token, json_web_key, "RS256"))
access_claims["iss"].should.equal(issuer) access_claims["iss"].should.equal(issuer)
access_claims["aud"].should.equal(client_id) access_claims["aud"].should.equal(client_id)
for k, v in outputs["additional_fields"].items(): access_claims["token_use"].should.equal("access")
access_claims[k].should.equal(v)
@mock_cognitoidp @mock_cognitoidp

View File

@ -9,7 +9,7 @@ from boto3.dynamodb.conditions import Attr, Key
import sure # noqa import sure # noqa
import requests import requests
from moto import mock_dynamodb2, mock_dynamodb2_deprecated from moto import mock_dynamodb2, mock_dynamodb2_deprecated
from moto.dynamodb2 import dynamodb_backend2 from moto.dynamodb2 import dynamodb_backend2, dynamodb_backends2
from boto.exception import JSONResponseError from boto.exception import JSONResponseError
from botocore.exceptions import ClientError, ParamValidationError from botocore.exceptions import ClientError, ParamValidationError
from tests.helpers import requires_boto_gte from tests.helpers import requires_boto_gte
@ -350,6 +350,60 @@ def test_put_item_with_special_chars():
) )
@requires_boto_gte("2.9")
@mock_dynamodb2
def test_put_item_with_streams():
name = "TestTable"
conn = boto3.client(
"dynamodb",
region_name="us-west-2",
aws_access_key_id="ak",
aws_secret_access_key="sk",
)
conn.create_table(
TableName=name,
KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
StreamSpecification={
"StreamEnabled": True,
"StreamViewType": "NEW_AND_OLD_IMAGES",
},
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
conn.put_item(
TableName=name,
Item={
"forum_name": {"S": "LOLCat Forum"},
"subject": {"S": "Check this out!"},
"Body": {"S": "http://url_to_lolcat.gif"},
"SentBy": {"S": "test"},
"Data": {"M": {"Key1": {"S": "Value1"}, "Key2": {"S": "Value2"}}},
},
)
result = conn.get_item(TableName=name, Key={"forum_name": {"S": "LOLCat Forum"}})
result["Item"].should.be.equal(
{
"forum_name": {"S": "LOLCat Forum"},
"subject": {"S": "Check this out!"},
"Body": {"S": "http://url_to_lolcat.gif"},
"SentBy": {"S": "test"},
"Data": {"M": {"Key1": {"S": "Value1"}, "Key2": {"S": "Value2"}}},
}
)
table = dynamodb_backends2["us-west-2"].get_table(name)
if not table:
# There is no way to access stream data over the API, so this part can't run in server-tests mode.
return
len(table.stream_shard.items).should.be.equal(1)
stream_record = table.stream_shard.items[0].record
stream_record["eventName"].should.be.equal("INSERT")
stream_record["dynamodb"]["SizeBytes"].should.be.equal(447)
@requires_boto_gte("2.9") @requires_boto_gte("2.9")
@mock_dynamodb2 @mock_dynamodb2
def test_query_returns_consumed_capacity(): def test_query_returns_consumed_capacity():
@ -1665,6 +1719,32 @@ def test_scan_filter4():
assert response["Count"] == 0 assert response["Count"] == 0
@mock_dynamodb2
def test_scan_filter_should_not_return_non_existing_attributes():
table_name = "my-table"
item = {"partitionKey": "pk-2", "my-attr": 42}
# Create table
res = boto3.resource("dynamodb", region_name="us-east-1")
res.create_table(
TableName=table_name,
KeySchema=[{"AttributeName": "partitionKey", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "partitionKey", "AttributeType": "S"}],
BillingMode="PAY_PER_REQUEST",
)
table = res.Table(table_name)
# Insert items
table.put_item(Item={"partitionKey": "pk-1"})
table.put_item(Item=item)
# Verify a few operations
# Assert we only find the item that has this attribute
table.scan(FilterExpression=Attr("my-attr").lt(43))["Items"].should.equal([item])
table.scan(FilterExpression=Attr("my-attr").lte(42))["Items"].should.equal([item])
table.scan(FilterExpression=Attr("my-attr").gte(42))["Items"].should.equal([item])
table.scan(FilterExpression=Attr("my-attr").gt(41))["Items"].should.equal([item])
# Sanity check that we can't find the item if the FE is wrong
table.scan(FilterExpression=Attr("my-attr").gt(43))["Items"].should.equal([])
@mock_dynamodb2 @mock_dynamodb2
def test_bad_scan_filter(): def test_bad_scan_filter():
client = boto3.client("dynamodb", region_name="us-east-1") client = boto3.client("dynamodb", region_name="us-east-1")
@ -2451,6 +2531,48 @@ def test_condition_expressions():
) )
@mock_dynamodb2
def test_condition_expression_numerical_attribute():
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
dynamodb.create_table(
TableName="my-table",
KeySchema=[{"AttributeName": "partitionKey", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "partitionKey", "AttributeType": "S"}],
)
table = dynamodb.Table("my-table")
table.put_item(Item={"partitionKey": "pk-pos", "myAttr": 5})
table.put_item(Item={"partitionKey": "pk-neg", "myAttr": -5})
# try to update the item we put in the table using numerical condition expression
# Specifically, verify that we can compare with a zero-value
# First verify that > and >= work on positive numbers
update_numerical_con_expr(
key="pk-pos", con_expr="myAttr > :zero", res="6", table=table
)
update_numerical_con_expr(
key="pk-pos", con_expr="myAttr >= :zero", res="7", table=table
)
# Second verify that < and <= work on negative numbers
update_numerical_con_expr(
key="pk-neg", con_expr="myAttr < :zero", res="-4", table=table
)
update_numerical_con_expr(
key="pk-neg", con_expr="myAttr <= :zero", res="-3", table=table
)
def update_numerical_con_expr(key, con_expr, res, table):
table.update_item(
Key={"partitionKey": key},
UpdateExpression="ADD myAttr :one",
ExpressionAttributeValues={":zero": 0, ":one": 1},
ConditionExpression=con_expr,
)
table.get_item(Key={"partitionKey": key})["Item"]["myAttr"].should.equal(
Decimal(res)
)
@mock_dynamodb2 @mock_dynamodb2
def test_condition_expression__attr_doesnt_exist(): def test_condition_expression__attr_doesnt_exist():
client = boto3.client("dynamodb", region_name="us-east-1") client = boto3.client("dynamodb", region_name="us-east-1")
@ -3435,6 +3557,58 @@ def test_update_supports_nested_list_append_onto_another_list():
) )
@mock_dynamodb2
def test_update_supports_list_append_maps():
client = boto3.client("dynamodb", region_name="us-west-1")
client.create_table(
AttributeDefinitions=[
{"AttributeName": "id", "AttributeType": "S"},
{"AttributeName": "rid", "AttributeType": "S"},
],
TableName="TestTable",
KeySchema=[
{"AttributeName": "id", "KeyType": "HASH"},
{"AttributeName": "rid", "KeyType": "RANGE"},
],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
client.put_item(
TableName="TestTable",
Item={
"id": {"S": "nested_list_append"},
"rid": {"S": "range_key"},
"a": {"L": [{"M": {"b": {"S": "bar1"}}}]},
},
)
# Update item using list_append expression
client.update_item(
TableName="TestTable",
Key={"id": {"S": "nested_list_append"}, "rid": {"S": "range_key"}},
UpdateExpression="SET a = list_append(a, :i)",
ExpressionAttributeValues={":i": {"L": [{"M": {"b": {"S": "bar2"}}}]}},
)
# Verify item is appended to the existing list
result = client.query(
TableName="TestTable",
KeyConditionExpression="id = :i AND begins_with(rid, :r)",
ExpressionAttributeValues={
":i": {"S": "nested_list_append"},
":r": {"S": "range_key"},
},
)["Items"]
result.should.equal(
[
{
"a": {"L": [{"M": {"b": {"S": "bar1"}}}, {"M": {"b": {"S": "bar2"}}}]},
"rid": {"S": "range_key"},
"id": {"S": "nested_list_append"},
}
]
)
@mock_dynamodb2 @mock_dynamodb2
def test_update_catches_invalid_list_append_operation(): def test_update_catches_invalid_list_append_operation():
client = boto3.client("dynamodb", region_name="us-east-1") client = boto3.client("dynamodb", region_name="us-east-1")

View File

@ -11,7 +11,7 @@ from moto import mock_ec2, mock_ec2_deprecated
def test_describe_regions(): def test_describe_regions():
conn = boto.connect_ec2("the_key", "the_secret") conn = boto.connect_ec2("the_key", "the_secret")
regions = conn.get_all_regions() regions = conn.get_all_regions()
regions.should.have.length_of(16) len(regions).should.be.greater_than(1)
for region in regions: for region in regions:
region.endpoint.should.contain(region.name) region.endpoint.should.contain(region.name)
@ -32,7 +32,7 @@ def test_availability_zones():
def test_boto3_describe_regions(): def test_boto3_describe_regions():
ec2 = boto3.client("ec2", "us-east-1") ec2 = boto3.client("ec2", "us-east-1")
resp = ec2.describe_regions() resp = ec2.describe_regions()
resp["Regions"].should.have.length_of(16) len(resp["Regions"]).should.be.greater_than(1)
for rec in resp["Regions"]: for rec in resp["Regions"]:
rec["Endpoint"].should.contain(rec["RegionName"]) rec["Endpoint"].should.contain(rec["RegionName"])

View File

@ -3,13 +3,21 @@ import boto.ec2
import boto.ec2.autoscale import boto.ec2.autoscale
import boto.ec2.elb import boto.ec2.elb
import sure import sure
from boto3 import Session
from moto import mock_ec2_deprecated, mock_autoscaling_deprecated, mock_elb_deprecated from moto import mock_ec2_deprecated, mock_autoscaling_deprecated, mock_elb_deprecated
from moto.ec2 import ec2_backends from moto.ec2 import ec2_backends
def test_use_boto_regions(): def test_use_boto_regions():
boto_regions = {r.name for r in boto.ec2.regions()} boto_regions = set()
for region in Session().get_available_regions("ec2"):
boto_regions.add(region)
for region in Session().get_available_regions("ec2", partition_name="aws-us-gov"):
boto_regions.add(region)
for region in Session().get_available_regions("ec2", partition_name="aws-cn"):
boto_regions.add(region)
moto_regions = set(ec2_backends) moto_regions = set(ec2_backends)
moto_regions.should.equal(boto_regions) moto_regions.should.equal(boto_regions)

View File

@ -0,0 +1,23 @@
import boto3
from moto import mock_ec2_instance_connect
pubkey = """ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQDV5+voluw2zmzqpqCAqtsyoP01TQ8Ydx1eS1yD6wUsHcPqMIqpo57YxiC8XPwrdeKQ6GG6MC3bHsgXoPypGP0LyixbiuLTU31DnnqorcHt4bWs6rQa7dK2pCCflz2fhYRt5ZjqSNsAKivIbqkH66JozN0SySIka3kEV79GdB0BicioKeEJlCwM9vvxafyzjWf/z8E0lh4ni3vkLpIVJ0t5l+Qd9QMJrT6Is0SCQPVagTYZoi8+fWDoGsBa8vyRwDjEzBl28ZplKh9tSyDkRIYszWTpmK8qHiqjLYZBfAxXjGJbEYL1iig4ZxvbYzKEiKSBi1ZMW9iWjHfZDZuxXAmB
example
"""
@mock_ec2_instance_connect
def test_send_ssh_public_key():
client = boto3.client("ec2-instance-connect", region_name="us-east-1")
fake_request_id = "example-2a47-4c91-9700-e37e85162cb6"
response = client.send_ssh_public_key(
InstanceId="i-abcdefg12345",
InstanceOSUser="ec2-user",
SSHPublicKey=pubkey,
AvailabilityZone="us-east-1a",
)
assert response["RequestId"] == fake_request_id

View File

@ -94,6 +94,7 @@ def test_register_task_definition():
"logConfiguration": {"logDriver": "json-file"}, "logConfiguration": {"logDriver": "json-file"},
} }
], ],
networkMode="bridge",
tags=[ tags=[
{"key": "createdBy", "value": "moto-unittest"}, {"key": "createdBy", "value": "moto-unittest"},
{"key": "foo", "value": "bar"}, {"key": "foo", "value": "bar"},
@ -124,6 +125,7 @@ def test_register_task_definition():
response["taskDefinition"]["containerDefinitions"][0]["logConfiguration"][ response["taskDefinition"]["containerDefinitions"][0]["logConfiguration"][
"logDriver" "logDriver"
].should.equal("json-file") ].should.equal("json-file")
response["taskDefinition"]["networkMode"].should.equal("bridge")
@mock_ecs @mock_ecs

View File

@ -44,7 +44,7 @@ def test_describe_job():
joboutput.should.have.key("Tier").which.should.equal("Standard") joboutput.should.have.key("Tier").which.should.equal("Standard")
joboutput.should.have.key("StatusCode").which.should.equal("InProgress") joboutput.should.have.key("StatusCode").which.should.equal("InProgress")
joboutput.should.have.key("VaultARN").which.should.equal( joboutput.should.have.key("VaultARN").which.should.equal(
"arn:aws:glacier:RegionInfo:us-west-2:012345678901:vaults/my_vault" "arn:aws:glacier:us-west-2:012345678901:vaults/my_vault"
) )

View File

@ -1737,9 +1737,7 @@ def test_delete_saml_provider():
def test_create_role_defaults(): def test_create_role_defaults():
"""Tests default values""" """Tests default values"""
conn = boto3.client("iam", region_name="us-east-1") conn = boto3.client("iam", region_name="us-east-1")
conn.create_role( conn.create_role(RoleName="my-role", AssumeRolePolicyDocument="{}")
RoleName="my-role", AssumeRolePolicyDocument="{}",
)
# Get role: # Get role:
role = conn.get_role(RoleName="my-role")["Role"] role = conn.get_role(RoleName="my-role")["Role"]
@ -2672,3 +2670,33 @@ def test_get_account_summary():
"GroupsQuota": 300, "GroupsQuota": 300,
} }
) )
@mock_iam()
def test_list_user_tags():
"""Tests both setting a tags on a user in create_user and list_user_tags"""
conn = boto3.client("iam", region_name="us-east-1")
conn.create_user(UserName="kenny-bania")
conn.create_user(
UserName="jackie-chiles", Tags=[{"Key": "Sue-Allen", "Value": "Oh-Henry"}]
)
conn.create_user(
UserName="cosmo",
Tags=[
{"Key": "Stan", "Value": "The Caddy"},
{"Key": "like-a", "Value": "glove"},
],
)
response = conn.list_user_tags(UserName="kenny-bania")
response["Tags"].should.equal([])
response["IsTruncated"].should_not.be.ok
response = conn.list_user_tags(UserName="jackie-chiles")
response["Tags"].should.equal([{"Key": "Sue-Allen", "Value": "Oh-Henry"}])
response["IsTruncated"].should_not.be.ok
response = conn.list_user_tags(UserName="cosmo")
response["Tags"].should.equal(
[{"Key": "Stan", "Value": "The Caddy"}, {"Key": "like-a", "Value": "glove"}]
)
response["IsTruncated"].should_not.be.ok

View File

@ -862,6 +862,8 @@ def test_list_resource_record_sets_name_type_filters():
StartRecordName=all_records[start_with][1], StartRecordName=all_records[start_with][1],
) )
response["IsTruncated"].should.equal(False)
returned_records = [ returned_records = [
(record["Type"], record["Name"]) for record in response["ResourceRecordSets"] (record["Type"], record["Name"]) for record in response["ResourceRecordSets"]
] ]

View File

@ -1261,7 +1261,7 @@ def test_boto3_list_objects_truncated_response():
assert listed_object["Key"] == "one" assert listed_object["Key"] == "one"
assert resp["MaxKeys"] == 1 assert resp["MaxKeys"] == 1
assert resp["IsTruncated"] == True assert resp["IsTruncated"] == True
assert resp["Prefix"] == "None" assert resp.get("Prefix") is None
assert resp["Delimiter"] == "None" assert resp["Delimiter"] == "None"
assert "NextMarker" in resp assert "NextMarker" in resp
@ -1274,7 +1274,7 @@ def test_boto3_list_objects_truncated_response():
assert listed_object["Key"] == "three" assert listed_object["Key"] == "three"
assert resp["MaxKeys"] == 1 assert resp["MaxKeys"] == 1
assert resp["IsTruncated"] == True assert resp["IsTruncated"] == True
assert resp["Prefix"] == "None" assert resp.get("Prefix") is None
assert resp["Delimiter"] == "None" assert resp["Delimiter"] == "None"
assert "NextMarker" in resp assert "NextMarker" in resp
@ -1287,7 +1287,7 @@ def test_boto3_list_objects_truncated_response():
assert listed_object["Key"] == "two" assert listed_object["Key"] == "two"
assert resp["MaxKeys"] == 1 assert resp["MaxKeys"] == 1
assert resp["IsTruncated"] == False assert resp["IsTruncated"] == False
assert resp["Prefix"] == "None" assert resp.get("Prefix") is None
assert resp["Delimiter"] == "None" assert resp["Delimiter"] == "None"
assert "NextMarker" not in resp assert "NextMarker" not in resp

View File

@ -54,9 +54,10 @@ def test_deleting_subscriptions_by_deleting_topic():
]["Subscriptions"] ]["Subscriptions"]
subscriptions.should.have.length_of(1) subscriptions.should.have.length_of(1)
subscription = subscriptions[0] subscription = subscriptions[0]
subscription_arn = subscription["SubscriptionArn"]
subscription["TopicArn"].should.equal(topic_arn) subscription["TopicArn"].should.equal(topic_arn)
subscription["Protocol"].should.equal("http") subscription["Protocol"].should.equal("http")
subscription["SubscriptionArn"].should.contain(topic_arn) subscription_arn.should.contain(topic_arn)
subscription["Endpoint"].should.equal("http://example.com/") subscription["Endpoint"].should.equal("http://example.com/")
# Now delete the topic # Now delete the topic
@ -67,12 +68,25 @@ def test_deleting_subscriptions_by_deleting_topic():
topics = topics_json["ListTopicsResponse"]["ListTopicsResult"]["Topics"] topics = topics_json["ListTopicsResponse"]["ListTopicsResult"]["Topics"]
topics.should.have.length_of(0) topics.should.have.length_of(0)
# And there should be zero subscriptions left # And the subscription should still be left
subscriptions = conn.get_all_subscriptions()["ListSubscriptionsResponse"][
"ListSubscriptionsResult"
]["Subscriptions"]
subscriptions.should.have.length_of(1)
subscription = subscriptions[0]
subscription["SubscriptionArn"].should.equal(subscription_arn)
# Now delete hanging subscription
conn.unsubscribe(subscription_arn)
subscriptions = conn.get_all_subscriptions()["ListSubscriptionsResponse"][ subscriptions = conn.get_all_subscriptions()["ListSubscriptionsResponse"][
"ListSubscriptionsResult" "ListSubscriptionsResult"
]["Subscriptions"] ]["Subscriptions"]
subscriptions.should.have.length_of(0) subscriptions.should.have.length_of(0)
# Deleting it again should not result in any error
conn.unsubscribe(subscription_arn)
@mock_sns_deprecated @mock_sns_deprecated
def test_getting_subscriptions_by_topic(): def test_getting_subscriptions_by_topic():

View File

@ -97,34 +97,48 @@ def test_creating_subscription():
@mock_sns @mock_sns
def test_deleting_subscriptions_by_deleting_topic(): def test_unsubscribe_from_deleted_topic():
conn = boto3.client("sns", region_name="us-east-1") client = boto3.client("sns", region_name="us-east-1")
conn.create_topic(Name="some-topic") client.create_topic(Name="some-topic")
response = conn.list_topics() response = client.list_topics()
topic_arn = response["Topics"][0]["TopicArn"] topic_arn = response["Topics"][0]["TopicArn"]
conn.subscribe(TopicArn=topic_arn, Protocol="http", Endpoint="http://example.com/") client.subscribe(
TopicArn=topic_arn, Protocol="http", Endpoint="http://example.com/"
)
subscriptions = conn.list_subscriptions()["Subscriptions"] subscriptions = client.list_subscriptions()["Subscriptions"]
subscriptions.should.have.length_of(1) subscriptions.should.have.length_of(1)
subscription = subscriptions[0] subscription = subscriptions[0]
subscription_arn = subscription["SubscriptionArn"]
subscription["TopicArn"].should.equal(topic_arn) subscription["TopicArn"].should.equal(topic_arn)
subscription["Protocol"].should.equal("http") subscription["Protocol"].should.equal("http")
subscription["SubscriptionArn"].should.contain(topic_arn) subscription_arn.should.contain(topic_arn)
subscription["Endpoint"].should.equal("http://example.com/") subscription["Endpoint"].should.equal("http://example.com/")
# Now delete the topic # Now delete the topic
conn.delete_topic(TopicArn=topic_arn) client.delete_topic(TopicArn=topic_arn)
# And there should now be 0 topics # And there should now be 0 topics
topics_json = conn.list_topics() topics_json = client.list_topics()
topics = topics_json["Topics"] topics = topics_json["Topics"]
topics.should.have.length_of(0) topics.should.have.length_of(0)
# And there should be zero subscriptions left # And the subscription should still be left
subscriptions = conn.list_subscriptions()["Subscriptions"] subscriptions = client.list_subscriptions()["Subscriptions"]
subscriptions.should.have.length_of(1)
subscription = subscriptions[0]
subscription["SubscriptionArn"].should.equal(subscription_arn)
# Now delete hanging subscription
client.unsubscribe(SubscriptionArn=subscription_arn)
subscriptions = client.list_subscriptions()["Subscriptions"]
subscriptions.should.have.length_of(0) subscriptions.should.have.length_of(0)
# Deleting it again should not result in any error
client.unsubscribe(SubscriptionArn=subscription_arn)
@mock_sns @mock_sns
def test_getting_subscriptions_by_topic(): def test_getting_subscriptions_by_topic():

View File

@ -331,7 +331,20 @@ def test_delete_queue():
@mock_sqs @mock_sqs
def test_get_queue_attributes(): def test_get_queue_attributes():
client = boto3.client("sqs", region_name="us-east-1") client = boto3.client("sqs", region_name="us-east-1")
response = client.create_queue(QueueName="test-queue")
dlq_resp = client.create_queue(QueueName="test-dlr-queue")
dlq_arn1 = client.get_queue_attributes(QueueUrl=dlq_resp["QueueUrl"])["Attributes"][
"QueueArn"
]
response = client.create_queue(
QueueName="test-queue",
Attributes={
"RedrivePolicy": json.dumps(
{"deadLetterTargetArn": dlq_arn1, "maxReceiveCount": 2}
),
},
)
queue_url = response["QueueUrl"] queue_url = response["QueueUrl"]
response = client.get_queue_attributes(QueueUrl=queue_url) response = client.get_queue_attributes(QueueUrl=queue_url)
@ -356,6 +369,7 @@ def test_get_queue_attributes():
"ApproximateNumberOfMessages", "ApproximateNumberOfMessages",
"MaximumMessageSize", "MaximumMessageSize",
"QueueArn", "QueueArn",
"RedrivePolicy",
"VisibilityTimeout", "VisibilityTimeout",
], ],
) )
@ -366,6 +380,9 @@ def test_get_queue_attributes():
"MaximumMessageSize": "65536", "MaximumMessageSize": "65536",
"QueueArn": "arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID), "QueueArn": "arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID),
"VisibilityTimeout": "30", "VisibilityTimeout": "30",
"RedrivePolicy": json.dumps(
{"deadLetterTargetArn": dlq_arn1, "maxReceiveCount": 2}
),
} }
) )