Feature - CloudFront (#4640)
This commit is contained in:
parent
41de9b82ac
commit
2cf37a4b90
@ -398,6 +398,104 @@
|
||||
- [X] validate_template
|
||||
</details>
|
||||
|
||||
## cloudfront
|
||||
<details>
|
||||
<summary>4% implemented</summary>
|
||||
|
||||
- [ ] associate_alias
|
||||
- [ ] create_cache_policy
|
||||
- [ ] create_cloud_front_origin_access_identity
|
||||
- [X] create_distribution
|
||||
- [ ] create_distribution_with_tags
|
||||
- [ ] create_field_level_encryption_config
|
||||
- [ ] create_field_level_encryption_profile
|
||||
- [ ] create_function
|
||||
- [ ] create_invalidation
|
||||
- [ ] create_key_group
|
||||
- [ ] create_monitoring_subscription
|
||||
- [ ] create_origin_request_policy
|
||||
- [ ] create_public_key
|
||||
- [ ] create_realtime_log_config
|
||||
- [ ] create_response_headers_policy
|
||||
- [ ] create_streaming_distribution
|
||||
- [ ] create_streaming_distribution_with_tags
|
||||
- [ ] delete_cache_policy
|
||||
- [ ] delete_cloud_front_origin_access_identity
|
||||
- [X] delete_distribution
|
||||
- [ ] delete_field_level_encryption_config
|
||||
- [ ] delete_field_level_encryption_profile
|
||||
- [ ] delete_function
|
||||
- [ ] delete_key_group
|
||||
- [ ] delete_monitoring_subscription
|
||||
- [ ] delete_origin_request_policy
|
||||
- [ ] delete_public_key
|
||||
- [ ] delete_realtime_log_config
|
||||
- [ ] delete_response_headers_policy
|
||||
- [ ] delete_streaming_distribution
|
||||
- [ ] describe_function
|
||||
- [ ] get_cache_policy
|
||||
- [ ] get_cache_policy_config
|
||||
- [ ] get_cloud_front_origin_access_identity
|
||||
- [ ] get_cloud_front_origin_access_identity_config
|
||||
- [X] get_distribution
|
||||
- [ ] get_distribution_config
|
||||
- [ ] get_field_level_encryption
|
||||
- [ ] get_field_level_encryption_config
|
||||
- [ ] get_field_level_encryption_profile
|
||||
- [ ] get_field_level_encryption_profile_config
|
||||
- [ ] get_function
|
||||
- [ ] get_invalidation
|
||||
- [ ] get_key_group
|
||||
- [ ] get_key_group_config
|
||||
- [ ] get_monitoring_subscription
|
||||
- [ ] get_origin_request_policy
|
||||
- [ ] get_origin_request_policy_config
|
||||
- [ ] get_public_key
|
||||
- [ ] get_public_key_config
|
||||
- [ ] get_realtime_log_config
|
||||
- [ ] get_response_headers_policy
|
||||
- [ ] get_response_headers_policy_config
|
||||
- [ ] get_streaming_distribution
|
||||
- [ ] get_streaming_distribution_config
|
||||
- [ ] list_cache_policies
|
||||
- [ ] list_cloud_front_origin_access_identities
|
||||
- [ ] list_conflicting_aliases
|
||||
- [X] list_distributions
|
||||
- [ ] list_distributions_by_cache_policy_id
|
||||
- [ ] list_distributions_by_key_group
|
||||
- [ ] list_distributions_by_origin_request_policy_id
|
||||
- [ ] list_distributions_by_realtime_log_config
|
||||
- [ ] list_distributions_by_response_headers_policy_id
|
||||
- [ ] list_distributions_by_web_acl_id
|
||||
- [ ] list_field_level_encryption_configs
|
||||
- [ ] list_field_level_encryption_profiles
|
||||
- [ ] list_functions
|
||||
- [ ] list_invalidations
|
||||
- [ ] list_key_groups
|
||||
- [ ] list_origin_request_policies
|
||||
- [ ] list_public_keys
|
||||
- [ ] list_realtime_log_configs
|
||||
- [ ] list_response_headers_policies
|
||||
- [ ] list_streaming_distributions
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] publish_function
|
||||
- [ ] tag_resource
|
||||
- [ ] test_function
|
||||
- [ ] untag_resource
|
||||
- [ ] update_cache_policy
|
||||
- [ ] update_cloud_front_origin_access_identity
|
||||
- [ ] update_distribution
|
||||
- [ ] update_field_level_encryption_config
|
||||
- [ ] update_field_level_encryption_profile
|
||||
- [ ] update_function
|
||||
- [ ] update_key_group
|
||||
- [ ] update_origin_request_policy
|
||||
- [ ] update_public_key
|
||||
- [ ] update_realtime_log_config
|
||||
- [ ] update_response_headers_policy
|
||||
- [ ] update_streaming_distribution
|
||||
</details>
|
||||
|
||||
## cloudtrail
|
||||
<details>
|
||||
<summary>44% implemented</summary>
|
||||
@ -4711,7 +4809,6 @@
|
||||
- cloud9
|
||||
- cloudcontrol
|
||||
- clouddirectory
|
||||
- cloudfront
|
||||
- cloudhsm
|
||||
- cloudhsmv2
|
||||
- cloudsearch
|
||||
|
134
docs/docs/services/cloudfront.rst
Normal file
134
docs/docs/services/cloudfront.rst
Normal file
@ -0,0 +1,134 @@
|
||||
.. _implementedservice_cloudfront:
|
||||
|
||||
.. |start-h3| raw:: html
|
||||
|
||||
<h3>
|
||||
|
||||
.. |end-h3| raw:: html
|
||||
|
||||
</h3>
|
||||
|
||||
==========
|
||||
cloudfront
|
||||
==========
|
||||
|
||||
|start-h3| Example usage |end-h3|
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
@mock_cloudfront
|
||||
def test_cloudfront_behaviour:
|
||||
boto3.client("cloudfront")
|
||||
...
|
||||
|
||||
|
||||
|
||||
|start-h3| Implemented features for this service |end-h3|
|
||||
|
||||
- [ ] associate_alias
|
||||
- [ ] create_cache_policy
|
||||
- [ ] create_cloud_front_origin_access_identity
|
||||
- [X] create_distribution
|
||||
|
||||
This has been tested against an S3-distribution with the simplest possible configuration.
|
||||
Please raise an issue if we're not persisting/returning the correct attributes for your use-case.
|
||||
|
||||
|
||||
- [ ] create_distribution_with_tags
|
||||
- [ ] create_field_level_encryption_config
|
||||
- [ ] create_field_level_encryption_profile
|
||||
- [ ] create_function
|
||||
- [ ] create_invalidation
|
||||
- [ ] create_key_group
|
||||
- [ ] create_monitoring_subscription
|
||||
- [ ] create_origin_request_policy
|
||||
- [ ] create_public_key
|
||||
- [ ] create_realtime_log_config
|
||||
- [ ] create_response_headers_policy
|
||||
- [ ] create_streaming_distribution
|
||||
- [ ] create_streaming_distribution_with_tags
|
||||
- [ ] delete_cache_policy
|
||||
- [ ] delete_cloud_front_origin_access_identity
|
||||
- [X] delete_distribution
|
||||
|
||||
The IfMatch-value is ignored - any value is considered valid.
|
||||
Calling this function without a value is invalid, per AWS' behaviour
|
||||
|
||||
|
||||
- [ ] delete_field_level_encryption_config
|
||||
- [ ] delete_field_level_encryption_profile
|
||||
- [ ] delete_function
|
||||
- [ ] delete_key_group
|
||||
- [ ] delete_monitoring_subscription
|
||||
- [ ] delete_origin_request_policy
|
||||
- [ ] delete_public_key
|
||||
- [ ] delete_realtime_log_config
|
||||
- [ ] delete_response_headers_policy
|
||||
- [ ] delete_streaming_distribution
|
||||
- [ ] describe_function
|
||||
- [ ] get_cache_policy
|
||||
- [ ] get_cache_policy_config
|
||||
- [ ] get_cloud_front_origin_access_identity
|
||||
- [ ] get_cloud_front_origin_access_identity_config
|
||||
- [X] get_distribution
|
||||
- [ ] get_distribution_config
|
||||
- [ ] get_field_level_encryption
|
||||
- [ ] get_field_level_encryption_config
|
||||
- [ ] get_field_level_encryption_profile
|
||||
- [ ] get_field_level_encryption_profile_config
|
||||
- [ ] get_function
|
||||
- [ ] get_invalidation
|
||||
- [ ] get_key_group
|
||||
- [ ] get_key_group_config
|
||||
- [ ] get_monitoring_subscription
|
||||
- [ ] get_origin_request_policy
|
||||
- [ ] get_origin_request_policy_config
|
||||
- [ ] get_public_key
|
||||
- [ ] get_public_key_config
|
||||
- [ ] get_realtime_log_config
|
||||
- [ ] get_response_headers_policy
|
||||
- [ ] get_response_headers_policy_config
|
||||
- [ ] get_streaming_distribution
|
||||
- [ ] get_streaming_distribution_config
|
||||
- [ ] list_cache_policies
|
||||
- [ ] list_cloud_front_origin_access_identities
|
||||
- [ ] list_conflicting_aliases
|
||||
- [X] list_distributions
|
||||
|
||||
Pagination is not supported yet.
|
||||
|
||||
|
||||
- [ ] list_distributions_by_cache_policy_id
|
||||
- [ ] list_distributions_by_key_group
|
||||
- [ ] list_distributions_by_origin_request_policy_id
|
||||
- [ ] list_distributions_by_realtime_log_config
|
||||
- [ ] list_distributions_by_response_headers_policy_id
|
||||
- [ ] list_distributions_by_web_acl_id
|
||||
- [ ] list_field_level_encryption_configs
|
||||
- [ ] list_field_level_encryption_profiles
|
||||
- [ ] list_functions
|
||||
- [ ] list_invalidations
|
||||
- [ ] list_key_groups
|
||||
- [ ] list_origin_request_policies
|
||||
- [ ] list_public_keys
|
||||
- [ ] list_realtime_log_configs
|
||||
- [ ] list_response_headers_policies
|
||||
- [ ] list_streaming_distributions
|
||||
- [ ] list_tags_for_resource
|
||||
- [ ] publish_function
|
||||
- [ ] tag_resource
|
||||
- [ ] test_function
|
||||
- [ ] untag_resource
|
||||
- [ ] update_cache_policy
|
||||
- [ ] update_cloud_front_origin_access_identity
|
||||
- [ ] update_distribution
|
||||
- [ ] update_field_level_encryption_config
|
||||
- [ ] update_field_level_encryption_profile
|
||||
- [ ] update_function
|
||||
- [ ] update_key_group
|
||||
- [ ] update_origin_request_policy
|
||||
- [ ] update_public_key
|
||||
- [ ] update_realtime_log_config
|
||||
- [ ] update_response_headers_policy
|
||||
- [ ] update_streaming_distribution
|
||||
|
@ -43,6 +43,7 @@ mock_cloudformation = lazy_load(".cloudformation", "mock_cloudformation")
|
||||
mock_cloudformation_deprecated = lazy_load(
|
||||
".cloudformation", "mock_cloudformation_deprecated"
|
||||
)
|
||||
mock_cloudfront = lazy_load(".cloudfront", "mock_cloudfront")
|
||||
mock_cloudtrail = lazy_load(".cloudtrail", "mock_cloudtrail", boto3_name="cloudtrail")
|
||||
mock_cloudwatch = lazy_load(".cloudwatch", "mock_cloudwatch")
|
||||
mock_cloudwatch_deprecated = lazy_load(".cloudwatch", "mock_cloudwatch_deprecated")
|
||||
|
@ -13,6 +13,7 @@ backend_url_patterns = [
|
||||
("batch", re.compile("https?://batch\\.(.+)\\.amazonaws.com")),
|
||||
("budgets", re.compile("https?://budgets\\.amazonaws\\.com")),
|
||||
("cloudformation", re.compile("https?://cloudformation\\.(.+)\\.amazonaws\\.com")),
|
||||
("cloudfront", re.compile("https?://cloudfront\\.amazonaws\\.com")),
|
||||
("cloudtrail", re.compile("https?://cloudtrail\\.(.+)\\.amazonaws\\.com")),
|
||||
("cloudwatch", re.compile("https?://monitoring\\.(.+)\\.amazonaws.com")),
|
||||
("codecommit", re.compile("https?://codecommit\\.(.+)\\.amazonaws\\.com")),
|
||||
|
5
moto/cloudfront/__init__.py
Normal file
5
moto/cloudfront/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .models import cloudfront_backend
|
||||
from ..core.models import base_decorator
|
||||
|
||||
cloudfront_backends = {"global": cloudfront_backend}
|
||||
mock_cloudfront = base_decorator(cloudfront_backends)
|
81
moto/cloudfront/exceptions.py
Normal file
81
moto/cloudfront/exceptions.py
Normal file
@ -0,0 +1,81 @@
|
||||
from moto.core.exceptions import RESTError
|
||||
|
||||
EXCEPTION_RESPONSE = """<?xml version="1.0"?>
|
||||
<ErrorResponse xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>{{ error_type }}</Code>
|
||||
<Message>{{ message }}</Message>
|
||||
</Error>
|
||||
<{{ request_id_tag }}>30c0dedb-92b1-4e2b-9be4-1188e3ed86ab</{{ request_id_tag }}>
|
||||
</ErrorResponse>"""
|
||||
|
||||
|
||||
class CloudFrontException(RESTError):
|
||||
|
||||
code = 400
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("template", "cferror")
|
||||
self.templates["cferror"] = EXCEPTION_RESPONSE
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class OriginDoesNotExist(CloudFrontException):
|
||||
|
||||
code = 404
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
"NoSuchOrigin",
|
||||
message="One or more of your origins or origin groups do not exist.",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class InvalidOriginServer(CloudFrontException):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
"InvalidOrigin",
|
||||
message="The specified origin server does not exist or is not valid.",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class DomainNameNotAnS3Bucket(CloudFrontException):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
"InvalidArgument",
|
||||
message="The parameter Origin DomainName does not refer to a valid S3 bucket.",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class DistributionAlreadyExists(CloudFrontException):
|
||||
def __init__(self, dist_id, **kwargs):
|
||||
super().__init__(
|
||||
"DistributionAlreadyExists",
|
||||
message=f"The caller reference that you are using to create a distribution is associated with another distribution. Already exists: {dist_id}",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class InvalidIfMatchVersion(CloudFrontException):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
"InvalidIfMatchVersion",
|
||||
message="The If-Match version is missing or not valid for the resource.",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class NoSuchDistribution(CloudFrontException):
|
||||
|
||||
code = 404
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
"NoSuchDistribution",
|
||||
message="The specified distribution does not exist.",
|
||||
**kwargs,
|
||||
)
|
224
moto/cloudfront/models.py
Normal file
224
moto/cloudfront/models.py
Normal file
@ -0,0 +1,224 @@
|
||||
import random
|
||||
import string
|
||||
|
||||
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
|
||||
from uuid import uuid4
|
||||
|
||||
from .exceptions import (
|
||||
OriginDoesNotExist,
|
||||
InvalidOriginServer,
|
||||
DomainNameNotAnS3Bucket,
|
||||
DistributionAlreadyExists,
|
||||
InvalidIfMatchVersion,
|
||||
NoSuchDistribution,
|
||||
)
|
||||
|
||||
|
||||
class ActiveTrustedSigners:
|
||||
def __init__(self):
|
||||
self.enabled = False
|
||||
self.quantity = 0
|
||||
self.signers = []
|
||||
|
||||
|
||||
class ActiveTrustedKeyGroups:
|
||||
def __init__(self):
|
||||
self.enabled = False
|
||||
self.quantity = 0
|
||||
self.kg_key_pair_ids = []
|
||||
|
||||
|
||||
class LambdaFunctionAssociation:
|
||||
def __init__(self):
|
||||
self.arn = ""
|
||||
self.event_type = ""
|
||||
self.include_body = False
|
||||
|
||||
|
||||
class ForwardedValues:
|
||||
def __init__(self):
|
||||
self.query_string = ""
|
||||
self.whitelisted_names = []
|
||||
self.headers = []
|
||||
self.query_string_cache_keys = []
|
||||
|
||||
|
||||
class DefaultCacheBehaviour:
|
||||
def __init__(self, config):
|
||||
self.target_origin_id = config["TargetOriginId"]
|
||||
self.trusted_signers_enabled = False
|
||||
self.trusted_signers = []
|
||||
self.trusted_key_groups_enabled = False
|
||||
self.trusted_key_groups = []
|
||||
self.viewer_protocol_policy = config["ViewerProtocolPolicy"]
|
||||
self.allowed_methods = ["HEAD", "GET"]
|
||||
self.cached_methods = ["GET", "HEAD"]
|
||||
self.smooth_streaming = True
|
||||
self.compress = True
|
||||
self.lambda_function_associations = []
|
||||
self.function_associations = []
|
||||
self.field_level_encryption_id = ""
|
||||
self.forwarded_values = ForwardedValues()
|
||||
self.min_ttl = 0
|
||||
self.default_ttl = 0
|
||||
self.max_ttl = 0
|
||||
|
||||
|
||||
class Logging:
|
||||
def __init__(self):
|
||||
self.enabled = False
|
||||
|
||||
|
||||
class ViewerCertificate:
|
||||
def __init__(self):
|
||||
self.cloud_front_default_certificate = True
|
||||
self.min_protocol_version = "TLSv1"
|
||||
self.certificate_source = "cloudfront"
|
||||
|
||||
|
||||
class Origin:
|
||||
def __init__(self, origin):
|
||||
self.id = origin["Id"]
|
||||
self.domain_name = origin["DomainName"]
|
||||
self.custom_headers = []
|
||||
self.s3_access_identity = ""
|
||||
self.custom_origin = None
|
||||
self.origin_shield = None
|
||||
self.connection_attempts = 3
|
||||
self.connection_timeout = 10
|
||||
|
||||
if "S3OriginConfig" not in origin and "CustomOriginConfig" not in origin:
|
||||
raise InvalidOriginServer
|
||||
|
||||
if "S3OriginConfig" in origin:
|
||||
# Very rough validation
|
||||
if not self.domain_name.endswith("amazonaws.com"):
|
||||
raise DomainNameNotAnS3Bucket
|
||||
self.s3_access_identity = origin["S3OriginConfig"]["OriginAccessIdentity"]
|
||||
|
||||
|
||||
class DistributionConfig:
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.default_cache_behavior = DefaultCacheBehaviour(
|
||||
config["DefaultCacheBehavior"]
|
||||
)
|
||||
self.cache_behaviors = []
|
||||
self.custom_error_responses = []
|
||||
self.logging = Logging()
|
||||
self.enabled = False
|
||||
self.viewer_certificate = ViewerCertificate()
|
||||
self.geo_restriction_type = "none"
|
||||
self.geo_restrictions = []
|
||||
self.caller_reference = config.get("CallerReference", str(uuid4()))
|
||||
self.origins = config["Origins"]["Items"]["Origin"]
|
||||
if not isinstance(self.origins, list):
|
||||
self.origins = [self.origins]
|
||||
|
||||
# This check happens before any other Origins-validation
|
||||
if self.default_cache_behavior.target_origin_id not in [
|
||||
o["Id"] for o in self.origins
|
||||
]:
|
||||
raise OriginDoesNotExist
|
||||
|
||||
self.origins = [Origin(o) for o in self.origins]
|
||||
self.price_class = "PriceClass_All"
|
||||
self.http_version = "http2"
|
||||
self.is_ipv6_enabled = True
|
||||
|
||||
|
||||
class Distribution(BaseModel):
|
||||
@staticmethod
|
||||
def random_id(uppercase=True):
|
||||
ascii_set = string.ascii_uppercase if uppercase else string.ascii_lowercase
|
||||
chars = list(range(10)) + list(ascii_set)
|
||||
resource_id = random.choice(ascii_set) + "".join(
|
||||
str(random.choice(chars)) for _ in range(12)
|
||||
)
|
||||
return resource_id
|
||||
|
||||
def __init__(self, config):
|
||||
self.distribution_id = Distribution.random_id()
|
||||
self.arn = (
|
||||
f"arn:aws:cloudfront:{ACCOUNT_ID}:distribution/{self.distribution_id}"
|
||||
)
|
||||
self.distribution_config = DistributionConfig(config)
|
||||
self.active_trusted_signers = ActiveTrustedSigners()
|
||||
self.active_trusted_key_groups = ActiveTrustedKeyGroups()
|
||||
self.aliases = []
|
||||
self.origin_groups = []
|
||||
self.alias_icp_recordals = []
|
||||
self.last_modified_time = "2021-11-27T10:34:26.802Z"
|
||||
self.in_progress_invalidation_batches = 0
|
||||
self.has_active_trusted_key_groups = False
|
||||
self.status = "InProgress"
|
||||
self.domain_name = f"{Distribution.random_id(uppercase=False)}.cloudfront.net"
|
||||
|
||||
def advance(self):
|
||||
"""
|
||||
Advance the status of this Distribution, to mimick AWS' behaviour
|
||||
"""
|
||||
if self.status == "InProgress":
|
||||
self.status = "Deployed"
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
return f"https://cloudfront.amazonaws.com/2020-05-31/distribution/{self.distribution_id}"
|
||||
|
||||
@property
|
||||
def etag(self):
|
||||
return Distribution.random_id()
|
||||
|
||||
|
||||
class CloudFrontBackend(BaseBackend):
|
||||
def __init__(self):
|
||||
self.distributions = dict()
|
||||
|
||||
def create_distribution(self, distribution_config):
|
||||
"""
|
||||
This has been tested against an S3-distribution with the simplest possible configuration.
|
||||
Please raise an issue if we're not persisting/returning the correct attributes for your use-case.
|
||||
"""
|
||||
dist = Distribution(distribution_config)
|
||||
caller_reference = dist.distribution_config.caller_reference
|
||||
existing_dist = self._distribution_with_caller_reference(caller_reference)
|
||||
if existing_dist:
|
||||
raise DistributionAlreadyExists(existing_dist.distribution_id)
|
||||
self.distributions[dist.distribution_id] = dist
|
||||
return dist, dist.location, dist.etag
|
||||
|
||||
def get_distribution(self, distribution_id):
|
||||
if distribution_id not in self.distributions:
|
||||
raise NoSuchDistribution
|
||||
dist = self.distributions[distribution_id]
|
||||
dist.advance()
|
||||
return dist, dist.etag
|
||||
|
||||
def delete_distribution(self, distribution_id, if_match):
|
||||
"""
|
||||
The IfMatch-value is ignored - any value is considered valid.
|
||||
Calling this function without a value is invalid, per AWS' behaviour
|
||||
"""
|
||||
if not if_match:
|
||||
raise InvalidIfMatchVersion
|
||||
if distribution_id not in self.distributions:
|
||||
raise NoSuchDistribution
|
||||
del self.distributions[distribution_id]
|
||||
|
||||
def list_distributions(self):
|
||||
"""
|
||||
Pagination is not supported yet.
|
||||
"""
|
||||
for dist in self.distributions.values():
|
||||
dist.advance()
|
||||
return self.distributions.values()
|
||||
|
||||
def _distribution_with_caller_reference(self, reference):
|
||||
for dist in self.distributions.values():
|
||||
config = dist.distribution_config
|
||||
if config.caller_reference == reference:
|
||||
return dist
|
||||
return False
|
||||
|
||||
|
||||
cloudfront_backend = CloudFrontBackend()
|
514
moto/cloudfront/responses.py
Normal file
514
moto/cloudfront/responses.py
Normal file
@ -0,0 +1,514 @@
|
||||
import xmltodict
|
||||
|
||||
from functools import wraps
|
||||
from moto.core.responses import BaseResponse
|
||||
from .models import cloudfront_backend
|
||||
from .exceptions import CloudFrontException
|
||||
|
||||
|
||||
XMLNS = "http://cloudfront.amazonaws.com/doc/2020-05-31/"
|
||||
|
||||
|
||||
def error_handler(f):
|
||||
@wraps(f)
|
||||
def _wrapper(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except CloudFrontException as e:
|
||||
return e.code, e.get_headers(), e.get_body()
|
||||
|
||||
return _wrapper
|
||||
|
||||
|
||||
class CloudFrontResponse(BaseResponse):
|
||||
def _get_xml_body(self):
|
||||
return xmltodict.parse(self.body, dict_constructor=dict)
|
||||
|
||||
@error_handler
|
||||
def distributions(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "POST":
|
||||
return self.create_distribution()
|
||||
if request.method == "GET":
|
||||
return self.list_distributions()
|
||||
|
||||
def create_distribution(self):
|
||||
params = self._get_xml_body()
|
||||
distribution_config = params.get("DistributionConfig")
|
||||
distribution, location, e_tag = cloudfront_backend.create_distribution(
|
||||
distribution_config=distribution_config,
|
||||
)
|
||||
template = self.response_template(CREATE_DISTRIBUTION_TEMPLATE)
|
||||
response = template.render(distribution=distribution, xmlns=XMLNS)
|
||||
headers = {"ETag": e_tag, "Location": location}
|
||||
return 200, headers, response
|
||||
|
||||
def list_distributions(self):
|
||||
distributions = cloudfront_backend.list_distributions()
|
||||
template = self.response_template(LIST_TEMPLATE)
|
||||
response = template.render(distributions=distributions)
|
||||
return 200, {}, response
|
||||
|
||||
@error_handler
|
||||
def individual_distribution(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
distribution_id = full_url.split("/")[-1]
|
||||
if request.method == "DELETE":
|
||||
if_match = self._get_param("If-Match")
|
||||
cloudfront_backend.delete_distribution(distribution_id, if_match)
|
||||
return 204, {}, ""
|
||||
if request.method == "GET":
|
||||
dist, etag = cloudfront_backend.get_distribution(distribution_id)
|
||||
template = self.response_template(GET_DISTRIBUTION_TEMPLATE)
|
||||
response = template.render(distribution=dist, xmlns=XMLNS)
|
||||
return 200, {"ETag": etag}, response
|
||||
|
||||
|
||||
DIST_META_TEMPLATE = """
|
||||
<Id>{{ distribution.distribution_id }}</Id>
|
||||
<ARN>{{ distribution.arn }}</ARN>
|
||||
<Status>{{ distribution.status }}</Status>
|
||||
<LastModifiedTime>{{ distribution.last_modified_time }}</LastModifiedTime>
|
||||
<InProgressInvalidationBatches>{{ distribution.in_progress_invalidation_batches }}</InProgressInvalidationBatches>
|
||||
<DomainName>{{ distribution.domain_name }}</DomainName>
|
||||
"""
|
||||
|
||||
|
||||
DIST_CONFIG_TEMPLATE = """
|
||||
<CallerReference>{{ distribution.distribution_config.caller_reference }}</CallerReference>
|
||||
<Aliases>
|
||||
<Quantity>{{ distribution.distribution_config.aliases|length }}</Quantity>
|
||||
<Items>
|
||||
{% for alias in distribution.distribution_config.aliases %}
|
||||
<CNAME>{{ alias }}</CNAME>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</Aliases>
|
||||
<DefaultRootObject>{{ distribution.distribution_config.default_distribution_object }}</DefaultRootObject>
|
||||
<Origins>
|
||||
<Quantity>{{ distribution.distribution_config.origins|length }}</Quantity>
|
||||
<Items>
|
||||
{% for origin in distribution.distribution_config.origins %}
|
||||
<Origin>
|
||||
<Id>{{ origin.id }}</Id>
|
||||
<DomainName>{{ origin.domain_name }}</DomainName>
|
||||
<OriginPath>{{ origin.origin_path }}</OriginPath>
|
||||
<CustomHeaders>
|
||||
<Quantity>{{ origin.custom_headers|length }}</Quantity>
|
||||
<Items>
|
||||
{% for header in origin.custom_headers %}
|
||||
<HeaderName>{{ header.header_name }}</HeaderName>
|
||||
<HeaderValue>{{ header.header_value }}</HeaderValue>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</CustomHeaders>
|
||||
<S3OriginConfig>
|
||||
<OriginAccessIdentity>{{ origin.s3_access_identity }}</OriginAccessIdentity>
|
||||
</S3OriginConfig>
|
||||
{% if origin.custom_origin %}
|
||||
<CustomOriginConfig>
|
||||
<HTTPPort>{{ origin.custom_origin.http_port }}</HTTPPort>
|
||||
<HTTPSPort>{{ origin.custom_origin.https_port }}</HTTPSPort>
|
||||
<OriginProtocolPolicy>{{ OriginProtocolPolicy }}</OriginProtocolPolicy>
|
||||
<OriginSslProtocols>
|
||||
<Quantity>{{ origin.custom_origin.origin_ssl_protocols.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for protocol in origin.custom_origin.origin_ssl_protocols %}
|
||||
{{ protocol }}
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</OriginSslProtocols>
|
||||
<OriginReadTimeout>{{ origin.custom_origin.origin_read_timeout }}</OriginReadTimeout>
|
||||
<OriginKeepaliveTimeout>{{ origin.custom_origin.origin_keepalive_timeout }}</OriginKeepaliveTimeout>
|
||||
</CustomOriginConfig>
|
||||
{% endif %}
|
||||
<ConnectionAttempts>{{ origin.connection_attempts }}</ConnectionAttempts>
|
||||
<ConnectionTimeout>{{ origin.connection_timeout }}</ConnectionTimeout>
|
||||
{% if origin.origin_shield %}
|
||||
<OriginShield>
|
||||
<Enabled>{{ origin.origin_shield.enabled }}</Enabled>
|
||||
<OriginShieldRegion>{{ OriginShieldRegion }}</OriginShieldRegion>
|
||||
</OriginShield>
|
||||
{% else %}
|
||||
<OriginShield>
|
||||
<Enabled>false</Enabled>
|
||||
</OriginShield>
|
||||
{% endif %}
|
||||
</Origin>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</Origins>
|
||||
<OriginGroups>
|
||||
<Quantity>{{ distribution.distribution_config.origin_groups|length }}</Quantity>
|
||||
{% if distribution.distribution_config.origin_groups %}
|
||||
<Items>
|
||||
{% for origin_group in distribution.distribution_config.origin_groups %}
|
||||
<Id>{{ origin_group.id }}</Id>
|
||||
<FailoverCriteria>
|
||||
<StatusCodes>
|
||||
<Quantity>{{ origin_group.failover_criteria.status_codes.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for status_code_list in origin_group_list.failover_criteria.status_codes.StatusCodeList %}
|
||||
<StatusCode>{{ status_code_list.status_code }}</StatusCode>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</StatusCodes>
|
||||
</FailoverCriteria>
|
||||
<Members>
|
||||
<Quantity>{{ origin_group.members.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for origin_group_member_list in origin_group.members.OriginGroupMemberList %}
|
||||
<OriginId>{{ origin_group_member_list.origin_id }}</OriginId>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</Members>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
{% endif %}
|
||||
</OriginGroups>
|
||||
<DefaultCacheBehavior>
|
||||
<TargetOriginId>{{ distribution.distribution_config.default_cache_behavior.target_origin_id }}</TargetOriginId>
|
||||
<TrustedSigners>
|
||||
<Enabled>{{ distribution.distribution_config.default_cache_behavior.trusted_signers.enabled }}</Enabled>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.trusted_signers|length }}</Quantity>
|
||||
<Items>
|
||||
{% for aws_account_number in distribution.distribution_config.default_cache_behavior.trusted_signers %}
|
||||
<AwsAccountNumber>{{ aws_account_number }}</AwsAccountNumber>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</TrustedSigners>
|
||||
<TrustedKeyGroups>
|
||||
<Enabled>{{ distribution.distribution_config.default_cache_behavior.trusted_key_groups_enabled }}</Enabled>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.trusted_key_groups|length }}</Quantity>
|
||||
<Items>
|
||||
{% for key_group in distribution.distribution_config.default_cache_behavior.trusted_key_groups %}
|
||||
<KeyGroup>{{ key_group }}</KeyGroup>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</TrustedKeyGroups>
|
||||
<ViewerProtocolPolicy>{{ distribution.distribution_config.default_cache_behavior.viewer_protocol_policy }}</ViewerProtocolPolicy>
|
||||
<AllowedMethods>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.allowed_methods|length }}</Quantity>
|
||||
<Items>
|
||||
{% for method in distribution.distribution_config.default_cache_behavior.allowed_methods %}
|
||||
<member>{{ method }}</member>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
<CachedMethods>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.cached_methods|length }}</Quantity>
|
||||
<Items>
|
||||
{% for method in distribution.distribution_config.default_cache_behavior.cached_methods %}
|
||||
<member>{{ method }}</member>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</CachedMethods>
|
||||
</AllowedMethods>
|
||||
<SmoothStreaming>{{ distribution.distribution_config.default_cache_behavior.smooth_streaming }}</SmoothStreaming>
|
||||
<Compress>{{ 'true' if distribution.distribution_config.default_cache_behavior.compress else 'false' }}</Compress>
|
||||
<LambdaFunctionAssociations>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.lambda_function_associations|length }}</Quantity>
|
||||
{% if distribution.distribution_config.default_cache_behavior.lambda_function_associations %}
|
||||
<Items>
|
||||
{% for func in distribution.distribution_config.default_cache_behavior.lambda_function_associations %}
|
||||
<LambdaFunctionARN>{{ func.arn }}</LambdaFunctionARN>
|
||||
<EventType>{{ func.event_type }}</EventType>
|
||||
<IncludeBody>{{ func.include_body }}</IncludeBody>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
{% endif %}
|
||||
</LambdaFunctionAssociations>
|
||||
<FunctionAssociations>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.function_associations|length }}</Quantity>
|
||||
{% if distribution.distribution_config.default_cache_behavior.function_associations %}
|
||||
<Items>
|
||||
{% for func in distribution.distribution_config.default_cache_behavior.function_associations %}
|
||||
<FunctionARN>{{ func.arn }}</FunctionARN>
|
||||
<EventType>{{ func.event_type }}</EventType>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
{% endif %}
|
||||
</FunctionAssociations>
|
||||
<FieldLevelEncryptionId>{{ distribution.distribution_config.default_cache_behavior.field_level_encryption_id }}</FieldLevelEncryptionId>
|
||||
<RealtimeLogConfigArn>{{ distribution.distribution_config.default_cache_behavior.realtime_log_config_arn }}</RealtimeLogConfigArn>
|
||||
<CachePolicyId>{{ distribution.distribution_config.default_cache_behavior.cache_policy_id }}</CachePolicyId>
|
||||
<OriginRequestPolicyId>{{ distribution.distribution_config.default_cache_behavior.origin_request_policy_id }}</OriginRequestPolicyId>
|
||||
<ResponseHeadersPolicyId>{{ distribution.distribution_config.default_cache_behavior.response_headers_policy_id }}</ResponseHeadersPolicyId>
|
||||
<ForwardedValues>
|
||||
<QueryString>{{ distribution.distribution_config.default_cache_behavior.forwarded_values.query_string }}</QueryString>
|
||||
<Cookies>
|
||||
<Forward>{{ ItemSelection }}</Forward>
|
||||
<WhitelistedNames>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.forwarded_values.whitelisted_names|length }}</Quantity>
|
||||
<Items>
|
||||
{% for name in distribution.distribution_config.default_cache_behavior.forwarded_values.whitelisted_names %}
|
||||
<Name>{{ name }}</Name>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</WhitelistedNames>
|
||||
</Cookies>
|
||||
<Headers>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.forwarded_values.headers|length }}</Quantity>
|
||||
<Items>
|
||||
{% for h in distribution.distribution_config.default_cache_behavior.forwarded_values.headers %}
|
||||
<Name>{{ h }}</Name>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</Headers>
|
||||
<QueryStringCacheKeys>
|
||||
<Quantity>{{ distribution.distribution_config.default_cache_behavior.forwarded_values.query_string_cache_keys|length }}</Quantity>
|
||||
<Items>
|
||||
{% for key in distribution.distribution_config.default_cache_behavior.forwarded_values.query_string_cache_keys %}
|
||||
<Name>{{ key }}</Name>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</QueryStringCacheKeys>
|
||||
</ForwardedValues>
|
||||
<MinTTL>{{ distribution.distribution_config.default_cache_behavior.min_ttl }}</MinTTL>
|
||||
<DefaultTTL>{{ distribution.distribution_config.default_cache_behavior.default_ttl }}</DefaultTTL>
|
||||
<MaxTTL>{{ distribution.distribution_config.default_cache_behavior.max_ttl }}</MaxTTL>
|
||||
</DefaultCacheBehavior>
|
||||
<CacheBehaviors>
|
||||
<Quantity>{{ distribution.distribution_config.cache_behaviors|length }}</Quantity>
|
||||
{% if distribution.distribution_config.cache_behaviors %}
|
||||
<Items>
|
||||
{% for behaviour in distribution.distribution_config.cache_behaviors %}
|
||||
<PathPattern>{{ behaviour.path_pattern }}</PathPattern>
|
||||
<TargetOriginId>{{ behaviour.target_origin_id }}</TargetOriginId>
|
||||
<TrustedSigners>
|
||||
<Enabled>{{ behaviour.trusted_signers.enabled }}</Enabled>
|
||||
<Quantity>{{ behaviour.trusted_signers.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for account_nr in behaviour.trusted_signers %}
|
||||
<AwsAccountNumber>{{ account_nr }}</AwsAccountNumber>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</TrustedSigners>
|
||||
<TrustedKeyGroups>
|
||||
<Enabled>{{ cache_behavior_list.trusted_key_groups.enabled }}</Enabled>
|
||||
<Quantity>{{ cache_behavior_list.trusted_key_groups.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for trusted_key_group_id_list in cache_behavior_list.trusted_key_groups.TrustedKeyGroupIdList %}
|
||||
<KeyGroup>{{ trusted_key_group_id_list.key_group }}</KeyGroup>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</TrustedKeyGroups>
|
||||
<ViewerProtocolPolicy>{{ ViewerProtocolPolicy }}</ViewerProtocolPolicy>
|
||||
<AllowedMethods>
|
||||
<Quantity>{{ cache_behavior_list.allowed_methods.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for methods_list in cache_behavior_list.allowed_methods.MethodsList %}{{ Method }}{% endfor %}
|
||||
</Items>
|
||||
<CachedMethods>
|
||||
<Quantity>{{ cache_behavior_list.allowed_methods.cached_methods.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for methods_list in cache_behavior_list.allowed_methods.cached_methods.MethodsList %}{{ Method }}{% endfor %}
|
||||
</Items>
|
||||
</CachedMethods>
|
||||
</AllowedMethods>
|
||||
<SmoothStreaming>{{ cache_behavior_list.smooth_streaming }}</SmoothStreaming>
|
||||
<Compress>{{ cache_behavior_list.compress }}</Compress>
|
||||
<LambdaFunctionAssociations>
|
||||
<Quantity>{{ cache_behavior_list.lambda_function_associations.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for lambda_function_association_list in cache_behavior_list.lambda_function_associations.LambdaFunctionAssociationList %}
|
||||
<LambdaFunctionARN>{{ LambdaFunctionARN }}</LambdaFunctionARN>
|
||||
<EventType>{{ EventType }}</EventType>
|
||||
<IncludeBody>{{ lambda_function_association_list.include_body }}</IncludeBody>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</LambdaFunctionAssociations>
|
||||
<FunctionAssociations>
|
||||
<Quantity>{{ cache_behavior_list.function_associations.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for function_association_list in cache_behavior_list.function_associations.FunctionAssociationList %}
|
||||
<FunctionARN>{{ FunctionARN }}</FunctionARN>
|
||||
<EventType>{{ EventType }}</EventType>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</FunctionAssociations>
|
||||
<FieldLevelEncryptionId>{{ cache_behavior_list.field_level_encryption_id }}</FieldLevelEncryptionId>
|
||||
<RealtimeLogConfigArn>{{ cache_behavior_list.realtime_log_config_arn }}</RealtimeLogConfigArn>
|
||||
<CachePolicyId>{{ cache_behavior_list.cache_policy_id }}</CachePolicyId>
|
||||
<OriginRequestPolicyId>{{ cache_behavior_list.origin_request_policy_id }}</OriginRequestPolicyId>
|
||||
<ResponseHeadersPolicyId>{{ cache_behavior_list.response_headers_policy_id }}</ResponseHeadersPolicyId>
|
||||
<ForwardedValues>
|
||||
<QueryString>{{ cache_behavior_list.forwarded_values.query_string }}</QueryString>
|
||||
<Cookies>
|
||||
<Forward>{{ ItemSelection }}</Forward>
|
||||
<WhitelistedNames>
|
||||
<Quantity>{{ cache_behavior_list.forwarded_values.cookies.whitelisted_names.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for cookie_name_list in cache_behavior_list.forwarded_values.cookies.whitelisted_names.CookieNameList %}
|
||||
<Name>{{ cookie_name_list.name }}</Name>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</WhitelistedNames>
|
||||
</Cookies>
|
||||
<Headers>
|
||||
<Quantity>{{ cache_behavior_list.forwarded_values.headers.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for header_list in cache_behavior_list.forwarded_values.headers.HeaderList %}
|
||||
<Name>{{ header_list.name }}</Name>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</Headers>
|
||||
<QueryStringCacheKeys>
|
||||
<Quantity>{{ cache_behavior_list.forwarded_values.query_string_cache_keys.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for query_string_cache_keys_list in cache_behavior_list.forwarded_values.query_string_cache_keys.QueryStringCacheKeysList %}
|
||||
<Name>{{ query_string_cache_keys_list.name }}</Name>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</QueryStringCacheKeys>
|
||||
</ForwardedValues>
|
||||
<MinTTL>{{ cache_behavior_list.min_ttl }}</MinTTL>
|
||||
<DefaultTTL>{{ cache_behavior_list.default_ttl }}</DefaultTTL>
|
||||
<MaxTTL>{{ cache_behavior_list.max_ttl }}</MaxTTL>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
{% endif %}
|
||||
</CacheBehaviors>
|
||||
<CustomErrorResponses>
|
||||
<Quantity>{{ distribution.distribution_config.custom_error_responses|length }}</Quantity>
|
||||
{% if distribution.distribution_config.custom_error_responses %}
|
||||
<Items>
|
||||
{% for response in distribution.distribution_config.custom_error_responses %}
|
||||
<ErrorCode>{{ response.error_code }}</ErrorCode>
|
||||
<ResponsePagePath>{{ response.response_page_path }}</ResponsePagePath>
|
||||
<ResponseCode>{{ response.response_code }}</ResponseCode>
|
||||
<ErrorCachingMinTTL>{{ response.error_caching_min_ttl }}</ErrorCachingMinTTL>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
{% endif %}
|
||||
</CustomErrorResponses>
|
||||
<Comment>{{ CommentType }}</Comment>
|
||||
<Logging>
|
||||
<Enabled>{{ distribution.distribution_config.logging.enabled }}</Enabled>
|
||||
<IncludeCookies>{{ distribution.distribution_config.logging.include_cookies }}</IncludeCookies>
|
||||
<Bucket>{{ distribution.distribution_config.logging.bucket }}</Bucket>
|
||||
<Prefix>{{ distribution.distribution_config.logging.prefix }}</Prefix>
|
||||
</Logging>
|
||||
<PriceClass>{{ distribution.distribution_config.price_class }}</PriceClass>
|
||||
<Enabled>{{ distribution.distribution_config.enabled }}</Enabled>
|
||||
<ViewerCertificate>
|
||||
<CloudFrontDefaultCertificate>{{ 'true' if distribution.distribution_config.viewer_certificate.cloud_front_default_certificate else 'false' }}</CloudFrontDefaultCertificate>
|
||||
<IAMCertificateId>{{ distribution.distribution_config.viewer_certificate.iam_certificate_id }}</IAMCertificateId>
|
||||
<ACMCertificateArn>{{ distribution.distribution_config.viewer_certificate.acm_certificate_arn }}</ACMCertificateArn>
|
||||
<SSLSupportMethod>{{ SSLSupportMethod }}</SSLSupportMethod>
|
||||
<MinimumProtocolVersion>{{ distribution.distribution_config.viewer_certificate.min_protocol_version }}</MinimumProtocolVersion>
|
||||
<Certificate>{{ distribution.distribution_config.viewer_certificate.certificate }}</Certificate>
|
||||
<CertificateSource>{{ distribution.distribution_config.viewer_certificate.certificate_source }}</CertificateSource>
|
||||
</ViewerCertificate>
|
||||
<Restrictions>
|
||||
<GeoRestriction>
|
||||
<RestrictionType>{{ distribution.distribution_config.geo_restriction_type }}</RestrictionType>
|
||||
<Quantity>{{ distribution.distribution_config.geo_restrictions|length }}</Quantity>
|
||||
{% if distribution.distribution_config.geo_restrictions %}
|
||||
<Items>
|
||||
{% for location in distribution.distribution_config.geo_restrictions %}
|
||||
<Location>{{ location }}</Location>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
{% endif %}
|
||||
</GeoRestriction>
|
||||
</Restrictions>
|
||||
<WebACLId>{{ distribution.distribution_config.web_acl_id }}</WebACLId>
|
||||
<HttpVersion>{{ distribution.distribution_config.http_version }}</HttpVersion>
|
||||
<IsIPV6Enabled>{{ 'true' if distribution.distribution_config.is_ipv6_enabled else 'false' }}</IsIPV6Enabled>
|
||||
"""
|
||||
|
||||
|
||||
DISTRIBUTION_TEMPLATE = (
|
||||
DIST_META_TEMPLATE
|
||||
+ """
|
||||
<ActiveTrustedSigners>
|
||||
<Enabled>{{ distribution.active_trusted_signers.enabled }}</Enabled>
|
||||
<Quantity>{{ distribution.active_trusted_signers.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for signer in distribution.active_trusted_signers.signers %}
|
||||
<AwsAccountNumber>{{ signer.aws_account_number }}</AwsAccountNumber>
|
||||
<KeyPairIds>
|
||||
<Quantity>{{ signer.key_pair_ids.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for key_pair_id_list in signer.key_pair_ids.KeyPairIdList %}
|
||||
<KeyPairId>{{ key_pair_id_list.key_pair_id }}</KeyPairId>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</KeyPairIds>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</ActiveTrustedSigners>
|
||||
<ActiveTrustedKeyGroups>
|
||||
<Enabled>{{ distribution.active_trusted_key_groups.enabled }}</Enabled>
|
||||
<Quantity>{{ distribution.active_trusted_key_groups.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for kg_key_pair_id in distribution.active_trusted_key_groups.kg_key_pair_ids %}
|
||||
<KeyGroupId>{{ kg_key_pair_id.key_group_id }}</KeyGroupId>
|
||||
<KeyPairIds>
|
||||
<Quantity>{{ kg_key_pair_id.key_pair_ids.quantity }}</Quantity>
|
||||
<Items>
|
||||
{% for key_pair_id_list in kg_key_pair_ids_list.key_pair_ids.KeyPairIdList %}
|
||||
<KeyPairId>{{ key_pair_id_list.key_pair_id }}</KeyPairId>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</KeyPairIds>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
</ActiveTrustedKeyGroups>
|
||||
<DistributionConfig>
|
||||
"""
|
||||
+ DIST_CONFIG_TEMPLATE
|
||||
+ """
|
||||
</DistributionConfig>
|
||||
<AliasICPRecordals>
|
||||
{% for a in distribution.alias_icp_recordals %}
|
||||
<CNAME>{{ a.cname }}</CNAME>
|
||||
<ICPRecordalStatus>{{ a.status }}</ICPRecordalStatus>
|
||||
{% endfor %}
|
||||
</AliasICPRecordals>"""
|
||||
)
|
||||
|
||||
CREATE_DISTRIBUTION_TEMPLATE = (
|
||||
"""<?xml version="1.0"?>
|
||||
<CreateDistributionResult xmlns="{{ xmlns }}">
|
||||
"""
|
||||
+ DISTRIBUTION_TEMPLATE
|
||||
+ """
|
||||
</CreateDistributionResult>
|
||||
"""
|
||||
)
|
||||
|
||||
GET_DISTRIBUTION_TEMPLATE = (
|
||||
"""<?xml version="1.0"?>
|
||||
<Distribution xmlns="{{ xmlns }}">
|
||||
"""
|
||||
+ DISTRIBUTION_TEMPLATE
|
||||
+ """
|
||||
</Distribution>
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
LIST_TEMPLATE = (
|
||||
"""<?xml version="1.0"?>
|
||||
<DistributionList xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/">
|
||||
<Marker></Marker>
|
||||
<MaxItems>100</MaxItems>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<Quantity>{{ distributions|length }}</Quantity>
|
||||
{% if distributions %}
|
||||
<Items>
|
||||
{% for distribution in distributions %}
|
||||
<DistributionSummary>
|
||||
"""
|
||||
+ DIST_META_TEMPLATE
|
||||
+ """
|
||||
"""
|
||||
+ DIST_CONFIG_TEMPLATE
|
||||
+ """
|
||||
</DistributionSummary>
|
||||
{% endfor %}
|
||||
</Items>
|
||||
{% endif %}
|
||||
</DistributionList>"""
|
||||
)
|
13
moto/cloudfront/urls.py
Normal file
13
moto/cloudfront/urls.py
Normal file
@ -0,0 +1,13 @@
|
||||
"""cloudfront base URL and path."""
|
||||
from .responses import CloudFrontResponse
|
||||
|
||||
response = CloudFrontResponse()
|
||||
|
||||
url_bases = [
|
||||
r"https?://cloudfront\.amazonaws\.com",
|
||||
]
|
||||
|
||||
url_paths = {
|
||||
"{0}/2020-05-31/distribution$": response.distributions,
|
||||
"{0}/2020-05-31/distribution/(?P<distribution_id>[^/]+)$": response.individual_distribution,
|
||||
}
|
@ -144,7 +144,7 @@ class convert_flask_to_httpretty_response(object):
|
||||
from flask import request, Response
|
||||
|
||||
try:
|
||||
result = self.callback(request, request.url, {})
|
||||
result = self.callback(request, request.url, dict(request.headers))
|
||||
except ClientError as exc:
|
||||
result = 400, {}, exc.response["Error"]["Message"]
|
||||
# result is a status, headers, response tuple
|
||||
|
@ -119,8 +119,8 @@ class DomainDispatcherApplication(object):
|
||||
# S3 is the last resort when the target is also unknown
|
||||
service, region = DEFAULT_SERVICE_REGION
|
||||
|
||||
if service == "budgets":
|
||||
# Budgets is global
|
||||
if service in ["budgets", "cloudfront"]:
|
||||
# Global Services - they do not have/expect a region
|
||||
host = f"{service}.amazonaws.com"
|
||||
elif service == "mediastore" and not target:
|
||||
# All MediaStore API calls have a target header
|
||||
|
0
tests/test_cloudfront/__init__.py
Normal file
0
tests/test_cloudfront/__init__.py
Normal file
415
tests/test_cloudfront/test_cloudfront_distributions.py
Normal file
415
tests/test_cloudfront/test_cloudfront_distributions.py
Normal file
@ -0,0 +1,415 @@
|
||||
import boto3
|
||||
|
||||
import pytest
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from moto import mock_cloudfront
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
|
||||
def example_distribution_config(ref):
|
||||
return {
|
||||
"CallerReference": ref,
|
||||
"Origins": {
|
||||
"Quantity": 1,
|
||||
"Items": [
|
||||
{
|
||||
"Id": "origin1",
|
||||
"DomainName": "asdf.s3.us-east-1.amazonaws.com",
|
||||
"S3OriginConfig": {"OriginAccessIdentity": ""},
|
||||
}
|
||||
],
|
||||
},
|
||||
"DefaultCacheBehavior": {
|
||||
"TargetOriginId": "origin1",
|
||||
"ViewerProtocolPolicy": "allow-all",
|
||||
"MinTTL": 10,
|
||||
"ForwardedValues": {"QueryString": False, "Cookies": {"Forward": "none",}},
|
||||
},
|
||||
"Comment": "an optional comment that's not actually optional",
|
||||
"Enabled": False,
|
||||
}
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_create_distribution_s3_minimum():
|
||||
client = boto3.client("cloudfront", region_name="us-west-1")
|
||||
|
||||
config = example_distribution_config("ref")
|
||||
resp = client.create_distribution(DistributionConfig=config)
|
||||
resp.should.have.key("Distribution")
|
||||
|
||||
distribution = resp["Distribution"]
|
||||
distribution.should.have.key("Id")
|
||||
distribution.should.have.key("ARN").equals(
|
||||
f"arn:aws:cloudfront:{ACCOUNT_ID}:distribution/{distribution['Id']}"
|
||||
)
|
||||
distribution.should.have.key("Status").equals("InProgress")
|
||||
distribution.should.have.key("LastModifiedTime")
|
||||
distribution.should.have.key("InProgressInvalidationBatches").equals(0)
|
||||
distribution.should.have.key("DomainName").should.contain(".cloudfront.net")
|
||||
|
||||
distribution.should.have.key("ActiveTrustedSigners")
|
||||
signers = distribution["ActiveTrustedSigners"]
|
||||
signers.should.have.key("Enabled").equals(False)
|
||||
signers.should.have.key("Quantity").equals(0)
|
||||
|
||||
distribution.should.have.key("ActiveTrustedKeyGroups")
|
||||
key_groups = distribution["ActiveTrustedKeyGroups"]
|
||||
key_groups.should.have.key("Enabled").equals(False)
|
||||
key_groups.should.have.key("Quantity").equals(0)
|
||||
|
||||
distribution.should.have.key("DistributionConfig")
|
||||
config = distribution["DistributionConfig"]
|
||||
config.should.have.key("CallerReference").should.equal("ref")
|
||||
|
||||
config.should.have.key("Aliases")
|
||||
config["Aliases"].should.have.key("Quantity").equals(0)
|
||||
|
||||
config.should.have.key("Origins")
|
||||
origins = config["Origins"]
|
||||
origins.should.have.key("Quantity").equals(1)
|
||||
origins.should.have.key("Items").length_of(1)
|
||||
origin = origins["Items"][0]
|
||||
origin.should.have.key("Id").equals("origin1")
|
||||
origin.should.have.key("DomainName").equals("asdf.s3.us-east-1.amazonaws.com")
|
||||
origin.should.have.key("OriginPath").equals("")
|
||||
|
||||
origin.should.have.key("CustomHeaders")
|
||||
origin["CustomHeaders"].should.have.key("Quantity").equals(0)
|
||||
|
||||
origin.should.have.key("ConnectionAttempts").equals(3)
|
||||
origin.should.have.key("ConnectionTimeout").equals(10)
|
||||
origin.should.have.key("OriginShield").equals({"Enabled": False})
|
||||
|
||||
config.should.have.key("OriginGroups").equals({"Quantity": 0})
|
||||
|
||||
config.should.have.key("DefaultCacheBehavior")
|
||||
default_cache = config["DefaultCacheBehavior"]
|
||||
default_cache.should.have.key("TargetOriginId").should.equal("origin1")
|
||||
default_cache.should.have.key("TrustedSigners")
|
||||
|
||||
signers = default_cache["TrustedSigners"]
|
||||
signers.should.have.key("Enabled").equals(False)
|
||||
signers.should.have.key("Quantity").equals(0)
|
||||
|
||||
default_cache.should.have.key("TrustedKeyGroups")
|
||||
groups = default_cache["TrustedKeyGroups"]
|
||||
groups.should.have.key("Enabled").equals(False)
|
||||
groups.should.have.key("Quantity").equals(0)
|
||||
|
||||
default_cache.should.have.key("ViewerProtocolPolicy").equals("allow-all")
|
||||
|
||||
default_cache.should.have.key("AllowedMethods")
|
||||
methods = default_cache["AllowedMethods"]
|
||||
methods.should.have.key("Quantity").equals(2)
|
||||
methods.should.have.key("Items")
|
||||
set(methods["Items"]).should.equal({"HEAD", "GET"})
|
||||
|
||||
methods.should.have.key("CachedMethods")
|
||||
cached_methods = methods["CachedMethods"]
|
||||
cached_methods.should.have.key("Quantity").equals(2)
|
||||
set(cached_methods["Items"]).should.equal({"HEAD", "GET"})
|
||||
|
||||
default_cache.should.have.key("SmoothStreaming").equals(False)
|
||||
default_cache.should.have.key("Compress").equals(True)
|
||||
default_cache.should.have.key("LambdaFunctionAssociations").equals({"Quantity": 0})
|
||||
default_cache.should.have.key("FunctionAssociations").equals({"Quantity": 0})
|
||||
default_cache.should.have.key("FieldLevelEncryptionId").equals("")
|
||||
default_cache.should.have.key("CachePolicyId")
|
||||
|
||||
config.should.have.key("CacheBehaviors").equals({"Quantity": 0})
|
||||
config.should.have.key("CustomErrorResponses").equals({"Quantity": 0})
|
||||
config.should.have.key("Comment").equals("")
|
||||
|
||||
config.should.have.key("Logging")
|
||||
logging = config["Logging"]
|
||||
logging.should.have.key("Enabled").equals(False)
|
||||
logging.should.have.key("IncludeCookies").equals(False)
|
||||
logging.should.have.key("Bucket").equals("")
|
||||
logging.should.have.key("Prefix").equals("")
|
||||
|
||||
config.should.have.key("PriceClass").equals("PriceClass_All")
|
||||
config.should.have.key("Enabled").equals(False)
|
||||
config.should.have.key("WebACLId")
|
||||
config.should.have.key("HttpVersion").equals("http2")
|
||||
config.should.have.key("IsIPV6Enabled").equals(True)
|
||||
|
||||
config.should.have.key("ViewerCertificate")
|
||||
cert = config["ViewerCertificate"]
|
||||
cert.should.have.key("CloudFrontDefaultCertificate").equals(True)
|
||||
cert.should.have.key("MinimumProtocolVersion").equals("TLSv1")
|
||||
cert.should.have.key("CertificateSource").equals("cloudfront")
|
||||
|
||||
config.should.have.key("Restrictions")
|
||||
config["Restrictions"].should.have.key("GeoRestriction")
|
||||
restriction = config["Restrictions"]["GeoRestriction"]
|
||||
restriction.should.have.key("RestrictionType").equals("none")
|
||||
restriction.should.have.key("Quantity").equals(0)
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_create_distribution_returns_etag():
|
||||
client = boto3.client("cloudfront", region_name="us-east-1")
|
||||
|
||||
config = example_distribution_config("ref")
|
||||
resp = client.create_distribution(DistributionConfig=config)
|
||||
dist_id = resp["Distribution"]["Id"]
|
||||
|
||||
headers = resp["ResponseMetadata"]["HTTPHeaders"]
|
||||
headers.should.have.key("etag").length_of(13)
|
||||
headers.should.have.key("location").equals(
|
||||
f"https://cloudfront.amazonaws.com/2020-05-31/distribution/{dist_id}"
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_create_distribution_needs_unique_caller_reference():
|
||||
client = boto3.client("cloudfront", region_name="us-east-1")
|
||||
|
||||
# Create standard distribution
|
||||
config = example_distribution_config(ref="ref")
|
||||
dist1 = client.create_distribution(DistributionConfig=config)
|
||||
dist1_id = dist1["Distribution"]["Id"]
|
||||
|
||||
# Try to create distribution with the same ref
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.create_distribution(DistributionConfig=config)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("DistributionAlreadyExists")
|
||||
err["Message"].should.equal(
|
||||
f"The caller reference that you are using to create a distribution is associated with another distribution. Already exists: {dist1_id}"
|
||||
)
|
||||
|
||||
# Creating another distribution with a different reference
|
||||
config = example_distribution_config(ref="ref2")
|
||||
dist2 = client.create_distribution(DistributionConfig=config)
|
||||
dist1_id.shouldnt.equal(dist2["Distribution"]["Id"])
|
||||
|
||||
# TODO: Verify two exist, using the list_distributions method
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_create_distribution_with_mismatched_originid():
|
||||
client = boto3.client("cloudfront", region_name="us-west-1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.create_distribution(
|
||||
DistributionConfig={
|
||||
"CallerReference": "ref",
|
||||
"Origins": {
|
||||
"Quantity": 1,
|
||||
"Items": [{"Id": "origin1", "DomainName": "https://getmoto.org",}],
|
||||
},
|
||||
"DefaultCacheBehavior": {
|
||||
"TargetOriginId": "asdf",
|
||||
"ViewerProtocolPolicy": "allow-all",
|
||||
},
|
||||
"Comment": "an optional comment that's not actually optional",
|
||||
"Enabled": False,
|
||||
}
|
||||
)
|
||||
metadata = exc.value.response["ResponseMetadata"]
|
||||
metadata["HTTPStatusCode"].should.equal(404)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("NoSuchOrigin")
|
||||
err["Message"].should.equal(
|
||||
"One or more of your origins or origin groups do not exist."
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_create_origin_without_origin_config():
|
||||
client = boto3.client("cloudfront", region_name="us-west-1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.create_distribution(
|
||||
DistributionConfig={
|
||||
"CallerReference": "ref",
|
||||
"Origins": {
|
||||
"Quantity": 1,
|
||||
"Items": [{"Id": "origin1", "DomainName": "https://getmoto.org",}],
|
||||
},
|
||||
"DefaultCacheBehavior": {
|
||||
"TargetOriginId": "origin1",
|
||||
"ViewerProtocolPolicy": "allow-all",
|
||||
},
|
||||
"Comment": "an optional comment that's not actually optional",
|
||||
"Enabled": False,
|
||||
}
|
||||
)
|
||||
|
||||
metadata = exc.value.response["ResponseMetadata"]
|
||||
metadata["HTTPStatusCode"].should.equal(400)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidOrigin")
|
||||
err["Message"].should.equal(
|
||||
"The specified origin server does not exist or is not valid."
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_create_distribution_with_invalid_s3_bucket():
|
||||
client = boto3.client("cloudfront", region_name="us-west-1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.create_distribution(
|
||||
DistributionConfig={
|
||||
"CallerReference": "ref",
|
||||
"Origins": {
|
||||
"Quantity": 1,
|
||||
"Items": [
|
||||
{
|
||||
"Id": "origin1",
|
||||
"DomainName": "https://getmoto.org",
|
||||
"S3OriginConfig": {"OriginAccessIdentity": ""},
|
||||
}
|
||||
],
|
||||
},
|
||||
"DefaultCacheBehavior": {
|
||||
"TargetOriginId": "origin1",
|
||||
"ViewerProtocolPolicy": "allow-all",
|
||||
},
|
||||
"Comment": "an optional comment that's not actually optional",
|
||||
"Enabled": False,
|
||||
}
|
||||
)
|
||||
|
||||
metadata = exc.value.response["ResponseMetadata"]
|
||||
metadata["HTTPStatusCode"].should.equal(400)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidArgument")
|
||||
err["Message"].should.equal(
|
||||
"The parameter Origin DomainName does not refer to a valid S3 bucket."
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_list_distributions_without_any():
|
||||
client = boto3.client("cloudfront", region_name="us-east-1")
|
||||
|
||||
resp = client.list_distributions()
|
||||
resp.should.have.key("DistributionList")
|
||||
dlist = resp["DistributionList"]
|
||||
dlist.should.have.key("Marker").equals("")
|
||||
dlist.should.have.key("MaxItems").equals(100)
|
||||
dlist.should.have.key("IsTruncated").equals(False)
|
||||
dlist.should.have.key("Quantity").equals(0)
|
||||
dlist.shouldnt.have.key("Items")
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_list_distributions():
|
||||
client = boto3.client("cloudfront", region_name="us-east-1")
|
||||
|
||||
config = example_distribution_config(ref="ref1")
|
||||
dist1 = client.create_distribution(DistributionConfig=config)["Distribution"]
|
||||
config = example_distribution_config(ref="ref2")
|
||||
dist2 = client.create_distribution(DistributionConfig=config)["Distribution"]
|
||||
|
||||
resp = client.list_distributions()
|
||||
resp.should.have.key("DistributionList")
|
||||
dlist = resp["DistributionList"]
|
||||
dlist.should.have.key("Quantity").equals(2)
|
||||
dlist.should.have.key("Items").length_of(2)
|
||||
|
||||
item1 = dlist["Items"][0]
|
||||
item1.should.have.key("Id").equals(dist1["Id"])
|
||||
item1.should.have.key("ARN")
|
||||
item1.should.have.key("Status").equals("Deployed")
|
||||
|
||||
item2 = dlist["Items"][1]
|
||||
item2.should.have.key("Id").equals(dist2["Id"])
|
||||
item2.should.have.key("ARN")
|
||||
item2.should.have.key("Status").equals("Deployed")
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_get_distribution():
|
||||
client = boto3.client("cloudfront", region_name="us-east-1")
|
||||
|
||||
# Create standard distribution
|
||||
config = example_distribution_config(ref="ref")
|
||||
dist = client.create_distribution(DistributionConfig=config)
|
||||
dist_id = dist["Distribution"]["Id"]
|
||||
|
||||
resp = client.get_distribution(Id=dist_id)
|
||||
|
||||
headers = resp["ResponseMetadata"]["HTTPHeaders"]
|
||||
headers.should.have.key("etag").length_of(13)
|
||||
dist = resp["Distribution"]
|
||||
dist.should.have.key("Id").equals(dist_id)
|
||||
dist.should.have.key("Status").equals("Deployed")
|
||||
dist.should.have.key("DomainName").equals(dist["DomainName"])
|
||||
|
||||
dist.should.have.key("DistributionConfig")
|
||||
config = dist["DistributionConfig"]
|
||||
config.should.have.key("CallerReference").should.equal("ref")
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_get_unknown_distribution():
|
||||
client = boto3.client("cloudfront", region_name="us-west-1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
# Should have a second param, IfMatch, that contains the ETag of the most recent GetDistribution-request
|
||||
client.get_distribution(Id="unknown")
|
||||
|
||||
metadata = exc.value.response["ResponseMetadata"]
|
||||
metadata["HTTPStatusCode"].should.equal(404)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("NoSuchDistribution")
|
||||
err["Message"].should.equal("The specified distribution does not exist.")
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_delete_unknown_distribution():
|
||||
client = boto3.client("cloudfront", region_name="us-west-1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.delete_distribution(Id="unknown", IfMatch="..")
|
||||
|
||||
metadata = exc.value.response["ResponseMetadata"]
|
||||
metadata["HTTPStatusCode"].should.equal(404)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("NoSuchDistribution")
|
||||
err["Message"].should.equal("The specified distribution does not exist.")
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_delete_distribution_without_ifmatch():
|
||||
client = boto3.client("cloudfront", region_name="us-west-1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
# Should have a second param, IfMatch, that contains the ETag of the most recent GetDistribution-request
|
||||
client.delete_distribution(Id="...")
|
||||
|
||||
metadata = exc.value.response["ResponseMetadata"]
|
||||
metadata["HTTPStatusCode"].should.equal(400)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidIfMatchVersion")
|
||||
err["Message"].should.equal(
|
||||
"The If-Match version is missing or not valid for the resource."
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudfront
|
||||
def test_delete_distribution_random_etag():
|
||||
"""
|
||||
Etag validation is not implemented yet
|
||||
Calling the delete-method with any etag will pass
|
||||
"""
|
||||
client = boto3.client("cloudfront", region_name="us-east-1")
|
||||
|
||||
# Create standard distribution
|
||||
config = example_distribution_config(ref="ref")
|
||||
dist1 = client.create_distribution(DistributionConfig=config)
|
||||
dist_id = dist1["Distribution"]["Id"]
|
||||
|
||||
client.delete_distribution(Id=dist_id, IfMatch="anything")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.get_distribution(Id=dist_id)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("NoSuchDistribution")
|
14
tests/test_cloudfront/test_server.py
Normal file
14
tests/test_cloudfront/test_server.py
Normal file
@ -0,0 +1,14 @@
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
import xmltodict
|
||||
|
||||
import moto.server as server
|
||||
|
||||
|
||||
def test_cloudfront_list():
|
||||
backend = server.create_backend_app("cloudfront")
|
||||
test_client = backend.test_client()
|
||||
|
||||
res = test_client.get("/2020-05-31/distribution")
|
||||
data = xmltodict.parse(res.data, dict_constructor=dict)
|
||||
data.should.have.key("DistributionList")
|
||||
data["DistributionList"].shouldnt.have.key("Items")
|
Loading…
Reference in New Issue
Block a user