diff --git a/INTEGRATION_TESTS.md b/INTEGRATION_TESTS.md index 6a5f4e487..6a75662a9 100644 --- a/INTEGRATION_TESTS.md +++ b/INTEGRATION_TESTS.md @@ -34,6 +34,14 @@ If you haven't done so already, run the following command in a terminal at the r make init ``` +### Setting up a companion stack + +To run the tests, a companion stack first needs to be created. This stack houses some resources that are required by the tests, such as an S3 bucket. + +``` +make prepare-companion-stack +``` + ### Running all the tests From the root of the repository, run: diff --git a/Makefile b/Makefile index 4692a5e59..78966d3fc 100755 --- a/Makefile +++ b/Makefile @@ -27,6 +27,9 @@ lint: # Linter performs static analysis to catch latent bugs pylint --rcfile .pylintrc samtranslator +prepare-companion-stack: + pytest -v --no-cov integration/setup -m setup + # Command to run everytime you make changes to verify everything works dev: test @@ -43,5 +46,6 @@ TARGETS integ-test Run the Integration tests. dev Run all development tests after a change. pr Perform all checks before submitting a Pull Request. + prepare-companion-stack Create or update the companion stack for running integration tests. endef diff --git a/integration/combination/test_api_settings.py b/integration/combination/test_api_settings.py index 5e1f11c0d..70fc59901 100644 --- a/integration/combination/test_api_settings.py +++ b/integration/combination/test_api_settings.py @@ -1,4 +1,8 @@ import hashlib +from unittest.case import skipIf + +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API try: from pathlib import Path @@ -10,6 +14,7 @@ from integration.helpers.base_test import BaseTest +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") class TestApiSettings(BaseTest): def test_method_settings(self): self.create_and_verify_stack("combination/api_with_method_settings") diff --git a/integration/combination/test_api_with_cors.py b/integration/combination/test_api_with_cors.py index abc3a5d06..414ed5d75 100644 --- a/integration/combination/test_api_with_cors.py +++ b/integration/combination/test_api_with_cors.py @@ -1,14 +1,17 @@ +from integration.helpers.base_test import BaseTest import requests +from unittest.case import skipIf from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API from integration.helpers.deployer.utils.retry import retry from parameterized import parameterized -from integration.helpers.exception import StatusCodeError - ALL_METHODS = "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT" +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") class TestApiWithCors(BaseTest): @parameterized.expand( [ @@ -26,8 +29,8 @@ def test_cors(self, file_name): allow_headers = "headers" max_age = "600" - self.verify_options_request(base_url + "/apione", allow_methods, allow_origin, allow_headers, max_age) - self.verify_options_request(base_url + "/apitwo", allow_methods, allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apione", allow_methods, allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apitwo", allow_methods, allow_origin, allow_headers, max_age) def test_cors_with_shorthand_notation(self): self.create_and_verify_stack("combination/api_with_cors_shorthand") @@ -38,8 +41,8 @@ def test_cors_with_shorthand_notation(self): allow_headers = None # This should be absent from response max_age = None # This should be absent from response - self.verify_options_request(base_url + "/apione", ALL_METHODS, allow_origin, allow_headers, max_age) - self.verify_options_request(base_url + "/apitwo", "OPTIONS,POST", allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apione", ALL_METHODS, allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apitwo", "OPTIONS,POST", allow_origin, allow_headers, max_age) def test_cors_with_only_methods(self): self.create_and_verify_stack("combination/api_with_cors_only_methods") @@ -51,8 +54,8 @@ def test_cors_with_only_methods(self): allow_headers = None # This should be absent from response max_age = None # This should be absent from response - self.verify_options_request(base_url + "/apione", allow_methods, allow_origin, allow_headers, max_age) - self.verify_options_request(base_url + "/apitwo", allow_methods, allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apione", allow_methods, allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apitwo", allow_methods, allow_origin, allow_headers, max_age) def test_cors_with_only_headers(self): self.create_and_verify_stack("combination/api_with_cors_only_headers") @@ -63,8 +66,8 @@ def test_cors_with_only_headers(self): allow_headers = "headers" max_age = None # This should be absent from response - self.verify_options_request(base_url + "/apione", ALL_METHODS, allow_origin, allow_headers, max_age) - self.verify_options_request(base_url + "/apitwo", "OPTIONS,POST", allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apione", ALL_METHODS, allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apitwo", "OPTIONS,POST", allow_origin, allow_headers, max_age) def test_cors_with_only_max_age(self): self.create_and_verify_stack("combination/api_with_cors_only_max_age") @@ -75,17 +78,12 @@ def test_cors_with_only_max_age(self): allow_headers = None max_age = "600" - self.verify_options_request(base_url + "/apione", ALL_METHODS, allow_origin, allow_headers, max_age) - self.verify_options_request(base_url + "/apitwo", "OPTIONS,POST", allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apione", ALL_METHODS, allow_origin, allow_headers, max_age) + self.verify_cors_options_request(base_url + "/apitwo", "OPTIONS,POST", allow_origin, allow_headers, max_age) - @retry(StatusCodeError, 3) - def verify_options_request(self, url, allow_methods, allow_origin, allow_headers, max_age): - response = requests.options(url) - status = response.status_code - if status != 200: - raise StatusCodeError("Request to {} failed with status: {}, expected status: 200".format(url, status)) + def verify_cors_options_request(self, url, allow_methods, allow_origin, allow_headers, max_age): + response = self.verify_options_request(url, 200) - self.assertEqual(status, 200, "Options request must be successful and return HTTP 200") headers = response.headers self.assertEqual( headers.get("Access-Control-Allow-Methods"), allow_methods, "Allow-Methods header must have proper value" diff --git a/integration/combination/test_api_with_fail_on_warnings.py b/integration/combination/test_api_with_fail_on_warnings.py new file mode 100644 index 000000000..b65fe0b6f --- /dev/null +++ b/integration/combination/test_api_with_fail_on_warnings.py @@ -0,0 +1,29 @@ +from parameterized import parameterized + +from integration.helpers.base_test import BaseTest + + +class TestApiWithFailOnWarnings(BaseTest): + @parameterized.expand( + [ + ("combination/api_with_fail_on_warnings", True), + ("combination/api_with_fail_on_warnings", False), + ] + ) + def test_end_point_configuration(self, file_name, disable_value): + parameters = [ + { + "ParameterKey": "FailOnWarningsValue", + "ParameterValue": "true" if disable_value else "false", + "UsePreviousValue": False, + "ResolvedValue": "string", + } + ] + + self.create_and_verify_stack(file_name, parameters) + + rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi") + apigw_client = self.client_provider.api_client + + api_result = apigw_client.get_rest_api(restApiId=rest_api_id) + self.assertEqual(api_result["ResponseMetadata"]["HTTPStatusCode"], 200) diff --git a/integration/combination/test_api_with_gateway_responses.py b/integration/combination/test_api_with_gateway_responses.py index c3efdc10f..39979e016 100644 --- a/integration/combination/test_api_with_gateway_responses.py +++ b/integration/combination/test_api_with_gateway_responses.py @@ -1,10 +1,14 @@ +import logging from unittest.case import skipIf +from tenacity import stop_after_attempt, retry_if_exception_type, after_log, wait_exponential, retry, wait_random + from integration.helpers.base_test import BaseTest -from integration.helpers.deployer.utils.retry import retry from integration.helpers.resource import current_region_does_not_support from integration.config.service_names import GATEWAY_RESPONSES +LOG = logging.getLogger(__name__) + @skipIf( current_region_does_not_support([GATEWAY_RESPONSES]), "GatewayResponses is not supported in this testing region" @@ -33,7 +37,13 @@ def test_gateway_responses(self): base_url = stack_outputs["ApiUrl"] self._verify_request_response_and_cors(base_url + "iam", 403) - @retry(AssertionError, exc_raise=AssertionError, exc_raise_msg="Unable to verify GatewayResponse request.") + @retry( + stop=stop_after_attempt(5), + wait=wait_exponential(multiplier=1, min=4, max=10) + wait_random(0, 1), + retry=retry_if_exception_type(AssertionError), + after=after_log(LOG, logging.WARNING), + reraise=True, + ) def _verify_request_response_and_cors(self, url, expected_response): response = self.verify_get_request_response(url, expected_response) access_control_allow_origin = response.headers.get("Access-Control-Allow-Origin", "") diff --git a/integration/combination/test_api_with_resource_policies.py b/integration/combination/test_api_with_resource_policies.py index 5021de9cd..366892e08 100644 --- a/integration/combination/test_api_with_resource_policies.py +++ b/integration/combination/test_api_with_resource_policies.py @@ -1,8 +1,12 @@ import json +from unittest.case import skipIf from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") class TestApiWithResourcePolicies(BaseTest): def test_api_resource_policies(self): self.create_and_verify_stack("combination/api_with_resource_policies") diff --git a/integration/combination/test_custom_http_api_domains_test.py b/integration/combination/test_custom_http_api_domains_test.py index cde5e2435..6aa66623d 100644 --- a/integration/combination/test_custom_http_api_domains_test.py +++ b/integration/combination/test_custom_http_api_domains_test.py @@ -2,7 +2,6 @@ from integration.config.service_names import CUSTOM_DOMAIN from integration.helpers.base_internal_test import BaseInternalTest -from integration.helpers.file_resources import FILE_TO_S3_URI_MAP from integration.helpers.resource import current_region_not_included @@ -25,7 +24,7 @@ def test_custom_http_api_domains_regional(self): self.assertEqual("httpapi.sam-gamma-regional.com", result["DomainName"]) mtls_auth_config = result["MutualTlsAuthentication"] - self.assertEqual(FILE_TO_S3_URI_MAP["MTLSCert.pem"]["uri"], mtls_auth_config["TruststoreUri"]) + self.assertEqual(self.file_to_s3_uri_map["MTLSCert.pem"]["uri"], mtls_auth_config["TruststoreUri"]) domain_name_configs = result["DomainNameConfigurations"] self.assertEqual(1, len(domain_name_configs)) diff --git a/integration/combination/test_custom_rest_api_domains.py b/integration/combination/test_custom_rest_api_domains.py index 79618aca8..f5319723a 100644 --- a/integration/combination/test_custom_rest_api_domains.py +++ b/integration/combination/test_custom_rest_api_domains.py @@ -2,7 +2,6 @@ from integration.config.service_names import CUSTOM_DOMAIN from integration.helpers.base_internal_test import BaseInternalTest -from integration.helpers.file_resources import FILE_TO_S3_URI_MAP from integration.helpers.resource import current_region_not_included @@ -47,7 +46,7 @@ def test_custom_rest_api_domains_regional(self): self.assertEqual("REGIONAL", end_point_types[0]) mtls_auth_config = result["mutualTlsAuthentication"] - self.assertEqual(FILE_TO_S3_URI_MAP["MTLSCert.pem"]["uri"], mtls_auth_config["truststoreUri"]) + self.assertEqual(self.file_to_s3_uri_map["MTLSCert.pem"]["uri"], mtls_auth_config["truststoreUri"]) def test_custom_rest_api_domains_regional_ownership_verification(self): self.create_and_verify_stack("combination/api_with_custom_domains_regional_ownership_verification") diff --git a/integration/combination/test_function_with_alias.py b/integration/combination/test_function_with_alias.py index 4018eadcc..e890e90a6 100644 --- a/integration/combination/test_function_with_alias.py +++ b/integration/combination/test_function_with_alias.py @@ -1,8 +1,11 @@ import json +from unittest.case import skipIf from botocore.exceptions import ClientError from integration.helpers.base_test import BaseTest, LOG from integration.helpers.common_api import get_function_versions +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API class TestFunctionWithAlias(BaseTest): @@ -82,6 +85,7 @@ def test_alias_in_globals_with_overrides(self): # add any extra runtime behavior that needs to be verified self.create_and_verify_stack("combination/function_with_alias_globals") + @skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") def test_alias_with_event_sources_get_correct_permissions(self): # There are two parts to testing Event Source integrations: # 1. Check if all event sources get wired to the alias diff --git a/integration/combination/test_function_with_api.py b/integration/combination/test_function_with_api.py index 523aa5e46..4ba22e46a 100644 --- a/integration/combination/test_function_with_api.py +++ b/integration/combination/test_function_with_api.py @@ -1,6 +1,11 @@ +from unittest.case import skipIf + from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") class TestFunctionWithApi(BaseTest): def test_function_with_api(self): self.create_and_verify_stack("combination/function_with_api") diff --git a/integration/combination/test_function_with_cwe_dlq_generated.py b/integration/combination/test_function_with_cwe_dlq_generated.py index 138da7476..de05fbd00 100644 --- a/integration/combination/test_function_with_cwe_dlq_generated.py +++ b/integration/combination/test_function_with_cwe_dlq_generated.py @@ -2,6 +2,7 @@ from unittest.case import skipIf from integration.helpers.base_test import BaseTest +from integration.helpers.common_api import get_queue_policy from integration.helpers.resource import first_item_in_dict, current_region_does_not_support from integration.config.service_names import CWE_CWS_DLQ @@ -30,9 +31,7 @@ def test_function_with_cwe(self): # checking if the generated dead-letter queue has necessary resource based policy attached to it sqs_client = self.client_provider.sqs_client - dlq_policy_result = sqs_client.get_queue_attributes(QueueUrl=lambda_target_dlq_url, AttributeNames=["Policy"]) - dlq_policy_doc = dlq_policy_result["Attributes"]["Policy"] - dlq_policy = json.loads(dlq_policy_doc)["Statement"] + dlq_policy = get_queue_policy(queue_url=lambda_target_dlq_url, sqs_client=sqs_client) self.assertEqual(len(dlq_policy), 1, "Only one statement must be in Dead-letter queue policy") dlq_policy_statement = dlq_policy[0] diff --git a/integration/combination/test_function_with_file_system_config.py b/integration/combination/test_function_with_file_system_config.py index 48d357b5b..e10cbbd1c 100644 --- a/integration/combination/test_function_with_file_system_config.py +++ b/integration/combination/test_function_with_file_system_config.py @@ -1,6 +1,23 @@ +from unittest.case import skipIf +import pytest + +from integration.config.service_names import EFS from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support class TestFunctionWithFileSystemConfig(BaseTest): + @pytest.fixture(autouse=True) + def companion_stack_outputs(self, get_companion_stack_outputs): + self.companion_stack_outputs = get_companion_stack_outputs + + @skipIf(current_region_does_not_support([EFS]), "EFS is not supported in this testing region") def test_function_with_efs_integration(self): - self.create_and_verify_stack("combination/function_with_file_system_config") + parameters = self.get_parameters(self.companion_stack_outputs) + self.create_and_verify_stack("combination/function_with_file_system_config", parameters) + + def get_parameters(self, dictionary): + parameters = [] + parameters.append(self.generate_parameter("PreCreatedSubnetOne", dictionary["PreCreatedSubnetOne"])) + parameters.append(self.generate_parameter("PreCreatedVpc", dictionary["PreCreatedVpc"])) + return parameters diff --git a/integration/combination/test_function_with_http_api.py b/integration/combination/test_function_with_http_api.py index 337edc429..a2741fd46 100644 --- a/integration/combination/test_function_with_http_api.py +++ b/integration/combination/test_function_with_http_api.py @@ -1,12 +1,18 @@ +import logging from unittest.case import skipIf +import pytest + from integration.helpers.base_test import BaseTest from integration.helpers.resource import current_region_does_not_support from integration.config.service_names import HTTP_API +LOG = logging.getLogger(__name__) + @skipIf(current_region_does_not_support([HTTP_API]), "HttpApi is not supported in this testing region") class TestFunctionWithHttpApi(BaseTest): + @pytest.mark.flaky(reruns=5) def test_function_with_http_api(self): self.create_and_verify_stack("combination/function_with_http_api") diff --git a/integration/combination/test_function_with_implicit_api_and_conditions.py b/integration/combination/test_function_with_implicit_api_and_conditions.py index 4c77fd201..c6fd02768 100644 --- a/integration/combination/test_function_with_implicit_api_and_conditions.py +++ b/integration/combination/test_function_with_implicit_api_and_conditions.py @@ -1,6 +1,11 @@ +from unittest.case import skipIf + from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") class TestFunctionWithImplicitApiAndCondition(BaseTest): def test_function_with_implicit_api_and_conditions(self): self.create_and_verify_stack("combination/function_with_implicit_api_and_conditions") diff --git a/integration/combination/test_intrinsic_function_support.py b/integration/combination/test_intrinsic_function_support.py index 05d9cd5ce..5b3460503 100644 --- a/integration/combination/test_intrinsic_function_support.py +++ b/integration/combination/test_intrinsic_function_support.py @@ -1,21 +1,31 @@ -from parameterized import parameterized +from unittest.case import skipIf +import pytest from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") class TestIntrinsicFunctionsSupport(BaseTest): + @pytest.fixture(autouse=True) + def companion_stack_outputs(self, get_companion_stack_outputs): + self.companion_stack_outputs = get_companion_stack_outputs - # test code definition uri object and serverless function properties support - @parameterized.expand( - [ - "combination/intrinsics_code_definition_uri", - "combination/intrinsics_serverless_function", - ] - ) - def test_common_support(self, file_name): + # test serverless function properties support + def test_serverless_function_property_support(self): # Just a simple deployment will validate that Code & Swagger files were accessible # Just a simple deployment will validate that all properties were resolved expected - self.create_and_verify_stack(file_name, self.get_default_test_template_parameters()) + parameters = self.get_parameters(self.companion_stack_outputs) + parameters.extend(self.get_default_test_template_parameters()) + self.create_and_verify_stack("combination/intrinsics_serverless_function", parameters) + + # test code definition uri object support + def test_definition_uri_support(self): + # Just a simple deployment will validate that Code & Swagger files were accessible + # Just a simple deployment will validate that all properties were resolved expected + parameters = self.get_default_test_template_parameters() + self.create_and_verify_stack("combination/intrinsics_code_definition_uri", parameters) def test_severless_api_properties_support(self): self.create_and_verify_stack( @@ -58,3 +68,9 @@ def test_severless_api_properties_support(self): self.assertEqual(tags["lambda:createdBy"], "SAM", "Expected 'SAM' tag value, but not found.") self.assertTrue("TagKey1" in tags) self.assertEqual(tags["TagKey1"], api_stage_name) + + def get_parameters(self, dictionary): + parameters = [] + parameters.append(self.generate_parameter("PreCreatedSubnetOne", dictionary["PreCreatedSubnetOne"])) + parameters.append(self.generate_parameter("PreCreatedVpc", dictionary["PreCreatedVpc"])) + return parameters diff --git a/integration/combination/test_resource_references.py b/integration/combination/test_resource_references.py index e7221ea41..1d3de57b7 100644 --- a/integration/combination/test_resource_references.py +++ b/integration/combination/test_resource_references.py @@ -1,7 +1,9 @@ -from integration.helpers.base_test import BaseTest - +from unittest.case import skipIf +from integration.helpers.base_test import BaseTest from integration.helpers.common_api import get_function_versions +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import REST_API # Tests resource references support of SAM Function resource @@ -35,6 +37,7 @@ def test_function_alias_references(self): self.assertEqual(stack_outputs["VersionNumber"], version_number) self.assertEqual(stack_outputs["VersionArn"], version_arn) + @skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") def test_api_with_resource_references(self): self.create_and_verify_stack("combination/api_with_resource_refs") diff --git a/integration/config/code_key_to_file_map.json b/integration/config/code_key_to_file_map.json new file mode 100644 index 000000000..75ab8c0d0 --- /dev/null +++ b/integration/config/code_key_to_file_map.json @@ -0,0 +1,8 @@ +{ + "codeuri": "code.zip", + "contenturi": "layer1.zip", + "definitionuri": "swagger1.json", + "templateurl": "template.yaml", + "binaryMediaCodeUri": "binary-media.zip", + "mtlsuri": "MTLSCert.pem" +} \ No newline at end of file diff --git a/integration/config/file_to_s3_map.json b/integration/config/file_to_s3_map.json new file mode 100644 index 000000000..c8096eefc --- /dev/null +++ b/integration/config/file_to_s3_map.json @@ -0,0 +1,11 @@ +{ + "code.zip": {"type": "s3", "uri": ""}, + "code2.zip": {"type": "s3", "uri": ""}, + "layer1.zip": {"type": "s3", "uri": ""}, + "swagger1.json": {"type": "s3", "uri": ""}, + "swagger2.json": {"type": "s3", "uri": ""}, + "binary-media.zip": {"type": "s3", "uri": ""}, + "template.yaml": {"type": "http", "uri": ""}, + "MTLSCert.pem": {"type": "s3", "uri": ""}, + "MTLSCert-Updated.pem": {"type": "s3", "uri": ""} +} \ No newline at end of file diff --git a/integration/config/region_service_exclusion.yaml b/integration/config/region_service_exclusion.yaml deleted file mode 100644 index a26d185ef..000000000 --- a/integration/config/region_service_exclusion.yaml +++ /dev/null @@ -1,231 +0,0 @@ -regions: - af-south-1: - - ServerlessRepo - - Cognito - - KMS - - CodeDeploy - - XRay - - IoT - - GatewayResponses - - HttpApi - - ARM - - MSK - - MQ - ap-east-1: - - Cognito - - IoT - - ServerlessRepo - - HttpApi - - ARM - ap-northeast-2: - - HttpApi - - ARM - ap-northeast-3: - - Cognito - - IoT - - ServerlessRepo - - XRay - - CodeDeploy - - HttpApi - - ARM - - MSK - - MQ - - EFS - - StateMachineCweCws - - CodeSign - ap-south-1: - - HttpApi - ap-southeast-3: - - ServerlessRepo - - CodeDeploy - - Cognito - - GatewayResponses - - Layers - - IoT - - XRay - - SQS - - HttpApi - - UsagePlans - - MSK - - MQ - - Kinesis - - DynamoDB - - CodeSign - - EFS - - CweCwsDlq - - StateMachineInlineDefinition - - StateMachineCweCws - - StateMachineWithApis - - LambdaEnvVars - ap-southeast-1: - - HttpApi - ca-central-1: - - Cognito - - IoT - - HttpApi - - ARM - cn-north-1: - - ServerlessRepo - - Cognito - - KMS - - CodeDeploy - - XRay - - IoT - - GatewayResponses - - HttpApi - - MSK - - MQ - - CodeSign - - CweCwsDlq - - Mode - - ARM - cn-northwest-1: - - ServerlessRepo - - Cognito - - KMS - - CodeDeploy - - XRay - - IoT - - GatewayResponses - - HttpApi - - ARM - - MSK - - MQ - - CodeSign - - CweCwsDlq - - Mode - eu-north-1: - - ServerlessRepo - - Cognito - - IoT - - HttpApi - - Layers - - ARM - eu-south-1: - - ServerlessRepo - - Cognito - - KMS - - CodeDeploy - - XRay - - IoT - - GatewayResponses - - HttpApi - - ARM - - MSK - - MQ - eu-west-2: - - HttpApi - eu-west-3: - - Cognito - - IoT - - XRay - - HttpApi - - ARM - me-south-1: - - ServerlessRepo - - Cognito - - IoT - - HttpApi - - ARM - sa-east-1: - - IoT - - Cognito - - HttpApi - - ARM - us-east-2: - - HttpApi - us-gov-east-1: - - ServerlessRepo - - Cognito - - XRay - - IoT - - GatewayResponses - - CodeDeploy - - HttpApi - - MSK - - MQ - - Kinesis - - DynamoDB - - CodeSign - - CweCwsDlq - - Mode - us-gov-west-1: - - ServerlessRepo - - Cognito - - XRay - - IoT - - GatewayResponses - - HttpApi - - MSK - - MQ - - Kinesis - - DynamoDB - - CodeSign - - CweCwsDlq - - Mode - us-iso-east-1: - - ServerlessRepo - - CodeDeploy - - Cognito - - GatewayResponses - - Layers - - IoT - - XRay - - SQS - - HttpApi - - UsagePlans - - MSK - - MQ - - Kinesis - - DynamoDB - - CodeSign - - EFS - - CweCwsDlq - - StateMachineInlineDefinition - - StateMachineCweCws - - StateMachineWithApis - - LambdaEnvVars - us-iso-west-1: - - ServerlessRepo - - CodeDeploy - - Cognito - - GatewayResponses - - Layers - - IoT - - XRay - - SQS - - HttpApi - - UsagePlans - - MSK - - MQ - - Kinesis - - DynamoDB - - CodeSign - - EFS - - CweCwsDlq - - StateMachineInlineDefinition - - StateMachineCweCws - - StateMachineWithApis - - LambdaEnvVars - us-isob-east-1: - - ServerlessRepo - - Cognito - - CodeDeploy - - XRay - - GatewayResponses - - Layers - - IoT - - SQS - - HttpApi - - UsagePlans - - MSK - - MQ - - Kinesis - - DynamoDB - - CodeSign - - EFS - - CweCwsDlq - - StateMachineInlineDefinition - us-west-1: - - Cognito - - IoT \ No newline at end of file diff --git a/integration/config/region_service_inclusion.yaml b/integration/config/region_service_inclusion.yaml deleted file mode 100644 index 14625b38d..000000000 --- a/integration/config/region_service_inclusion.yaml +++ /dev/null @@ -1,3 +0,0 @@ -regions: - us-east-1: - - CustomDomain \ No newline at end of file diff --git a/integration/config/service_names.py b/integration/config/service_names.py index 6862586a7..f74783f4c 100644 --- a/integration/config/service_names.py +++ b/integration/config/service_names.py @@ -4,6 +4,7 @@ XRAY = "XRay" LAYERS = "Layers" HTTP_API = "HttpApi" +REST_API = "RestApi" IOT = "IoT" CODE_DEPLOY = "CodeDeploy" ARM = "ARM" @@ -21,3 +22,4 @@ SQS = "SQS" CUSTOM_DOMAIN = "CustomDomain" ARM = "ARM" +EFS = "EFS" diff --git a/integration/conftest.py b/integration/conftest.py index d0a2782ee..fb6cca5ae 100644 --- a/integration/conftest.py +++ b/integration/conftest.py @@ -6,10 +6,11 @@ from integration.helpers.base_test import S3_BUCKET_PREFIX from integration.helpers.client_provider import ClientProvider -from integration.helpers.deployer.exceptions.exceptions import ThrottlingError +from integration.helpers.deployer.exceptions.exceptions import S3DoesNotExistException, ThrottlingError from integration.helpers.deployer.utils.retry import retry_with_exponential_backoff_and_jitter from integration.helpers.stack import Stack from integration.helpers.yaml_utils import load_yaml +from integration.helpers.resource import read_test_config_file, write_test_config_file_to_json try: from pathlib import Path @@ -63,10 +64,57 @@ def setup_companion_stack_once(tmpdir_factory, get_prefix): cfn_client = ClientProvider().cfn_client output_dir = tmpdir_factory.mktemp("data") stack_name = get_prefix + COMPANION_STACK_NAME - if _stack_exists(stack_name): - return companion_stack = Stack(stack_name, companion_stack_tempalte_path, cfn_client, output_dir) - companion_stack.create() + companion_stack.create_or_update(_stack_exists(stack_name)) + + +@pytest.fixture() +def upload_resources(get_s3): + """ + Creates the bucket and uploads the files used by the tests to it + """ + s3_bucket = get_s3 + if not _s3_exists(s3_bucket): + raise S3DoesNotExistException(get_s3, "Check companion stack status") + code_dir = Path(__file__).resolve().parents[0].joinpath("resources").joinpath("code") + file_to_s3_uri_map = read_test_config_file("file_to_s3_map.json") + + if not file_to_s3_uri_map or not file_to_s3_uri_map.items(): + LOG.debug("No resources to upload") + return + + current_file_name = "" + + try: + s3_client = ClientProvider().s3_client + session = boto3.session.Session() + region = session.region_name + for file_name, file_info in file_to_s3_uri_map.items(): + current_file_name = file_name + code_path = str(Path(code_dir, file_name)) + LOG.debug("Uploading file %s to bucket %s", file_name, s3_bucket) + s3_client.upload_file(code_path, s3_bucket, file_name) + LOG.debug("File %s uploaded successfully to bucket %s", file_name, s3_bucket) + file_info["uri"] = get_s3_uri(file_name, file_info["type"], s3_bucket, region) + except ClientError as error: + LOG.error("Upload of file %s to bucket %s failed", current_file_name, s3_bucket, exc_info=error) + raise error + + write_test_config_file_to_json("file_to_s3_map_modified.json", file_to_s3_uri_map) + + +def get_s3_uri(file_name, uri_type, bucket, region): + if uri_type == "s3": + return "s3://{}/{}".format(bucket, file_name) + + if region == "us-east-1": + return "https://s3.amazonaws.com/{}/{}".format(bucket, file_name) + if region == "us-iso-east-1": + return "https://s3.us-iso-east-1.c2s.ic.gov/{}/{}".format(bucket, file_name) + if region == "us-isob-east-1": + return "https://s3.us-isob-east-1.sc2s.sgov.gov/{}/{}".format(bucket, file_name) + + return "https://s3-{}.amazonaws.com/{}/{}".format(region, bucket, file_name) @pytest.fixture() @@ -99,6 +147,12 @@ def get_companion_stack_outputs(get_prefix): return get_stack_outputs(companion_stack_description) +@pytest.fixture() +def get_s3(get_companion_stack_outputs): + s3_bucket = get_companion_stack_outputs.get("PreCreatedS3Bucket") + return str(s3_bucket) + + @pytest.fixture() def get_prefix(request): prefix = "" @@ -171,3 +225,15 @@ def _stack_exists(stack_name): raise ex return True + + +@retry_with_exponential_backoff_and_jitter(ThrottlingError, 5, 360) +def _s3_exists(s3_bucket): + s3 = boto3.resource("s3") + bucket = s3.Bucket(s3_bucket) + try: + s3.meta.client.head_bucket(Bucket=bucket.name) + except ClientError: + return False + + return True diff --git a/integration/helpers/base_test.py b/integration/helpers/base_test.py index 2dfd2a99d..3b0e52aaf 100644 --- a/integration/helpers/base_test.py +++ b/integration/helpers/base_test.py @@ -10,12 +10,23 @@ from integration.helpers.client_provider import ClientProvider from integration.helpers.deployer.exceptions.exceptions import ThrottlingError from integration.helpers.deployer.utils.retry import retry_with_exponential_backoff_and_jitter +from integration.helpers.exception import StatusCodeError from integration.helpers.request_utils import RequestUtils from integration.helpers.resource import generate_suffix, create_bucket, verify_stack_resources from integration.helpers.s3_uploader import S3Uploader from integration.helpers.yaml_utils import dump_yaml, load_yaml +from integration.helpers.resource import read_test_config_file from samtranslator.yaml_helper import yaml_parse +from tenacity import ( + retry, + stop_after_attempt, + wait_exponential, + retry_if_exception_type, + after_log, + wait_random, +) + try: from pathlib import Path except ImportError: @@ -23,11 +34,9 @@ from unittest.case import TestCase import boto3 -from botocore.exceptions import ClientError from integration.helpers.deployer.deployer import Deployer from integration.helpers.template import transform_template -from integration.helpers.file_resources import FILE_TO_S3_URI_MAP, CODE_KEY_TO_FILE_MAP LOG = logging.getLogger(__name__) @@ -47,8 +56,12 @@ def prefix(self, get_prefix): def stage(self, get_stage): self.pipeline_stage = get_stage + @pytest.fixture(autouse=True) + def s3_bucket(self, get_s3): + self.s3_bucket_name = get_s3 + @classmethod - @pytest.mark.usefixtures("get_prefix", "get_stage", "check_internal", "parameter_values") + @pytest.mark.usefixtures("get_prefix", "get_stage", "check_internal", "parameter_values", "get_s3") def setUpClass(cls): cls.FUNCTION_OUTPUT = "hello" cls.tests_integ_dir = Path(__file__).resolve().parents[1] @@ -57,85 +70,19 @@ def setUpClass(cls): cls.output_dir = Path(cls.tests_integ_dir, "tmp" + "-" + generate_suffix()) cls.expected_dir = Path(cls.resources_dir, "expected") cls.code_dir = Path(cls.resources_dir, "code") - cls.s3_bucket_name = S3_BUCKET_PREFIX + generate_suffix() cls.session = boto3.session.Session() cls.my_region = cls.session.region_name cls.client_provider = ClientProvider() - cls.file_to_s3_uri_map = FILE_TO_S3_URI_MAP - cls.code_key_to_file = CODE_KEY_TO_FILE_MAP + cls.file_to_s3_uri_map = read_test_config_file("file_to_s3_map_modified.json") + cls.code_key_to_file = read_test_config_file("code_key_to_file_map.json") if not cls.output_dir.exists(): os.mkdir(str(cls.output_dir)) - cls._upload_resources(FILE_TO_S3_URI_MAP) - @classmethod def tearDownClass(cls): - cls._clean_bucket() shutil.rmtree(cls.output_dir) - @classmethod - def _clean_bucket(cls): - """ - Empties and deletes the bucket used for the tests - """ - s3 = boto3.resource("s3") - bucket = s3.Bucket(cls.s3_bucket_name) - object_summary_iterator = bucket.objects.all() - - for object_summary in object_summary_iterator: - try: - cls.client_provider.s3_client.delete_object(Key=object_summary.key, Bucket=cls.s3_bucket_name) - except ClientError as e: - LOG.error( - "Unable to delete object %s from bucket %s", object_summary.key, cls.s3_bucket_name, exc_info=e - ) - try: - cls.client_provider.s3_client.delete_bucket(Bucket=cls.s3_bucket_name) - except ClientError as e: - LOG.error("Unable to delete bucket %s", cls.s3_bucket_name, exc_info=e) - - @classmethod - def _upload_resources(cls, file_to_s3_uri_map): - """ - Creates the bucket and uploads the files used by the tests to it - """ - if not file_to_s3_uri_map or not file_to_s3_uri_map.items(): - LOG.debug("No resources to upload") - return - - create_bucket(cls.s3_bucket_name, region=cls.my_region) - - current_file_name = "" - - try: - for file_name, file_info in file_to_s3_uri_map.items(): - current_file_name = file_name - code_path = str(Path(cls.code_dir, file_name)) - LOG.debug("Uploading file %s to bucket %s", file_name, cls.s3_bucket_name) - s3_client = cls.client_provider.s3_client - s3_client.upload_file(code_path, cls.s3_bucket_name, file_name) - LOG.debug("File %s uploaded successfully to bucket %s", file_name, cls.s3_bucket_name) - file_info["uri"] = cls._get_s3_uri(file_name, file_info["type"]) - except ClientError as error: - LOG.error("Upload of file %s to bucket %s failed", current_file_name, cls.s3_bucket_name, exc_info=error) - cls._clean_bucket() - raise error - - @classmethod - def _get_s3_uri(cls, file_name, uri_type): - if uri_type == "s3": - return "s3://{}/{}".format(cls.s3_bucket_name, file_name) - - if cls.my_region == "us-east-1": - return "https://s3.amazonaws.com/{}/{}".format(cls.s3_bucket_name, file_name) - if cls.my_region == "us-iso-east-1": - return "https://s3.us-iso-east-1.c2s.ic.gov/{}/{}".format(cls.s3_bucket_name, file_name) - if cls.my_region == "us-isob-east-1": - return "https://s3.us-isob-east-1.sc2s.sgov.gov/{}/{}".format(cls.s3_bucket_name, file_name) - - return "https://s3-{}.amazonaws.com/{}/{}".format(cls.my_region, cls.s3_bucket_name, file_name) - def setUp(self): self.deployer = Deployer(self.client_provider.cfn_client) self.s3_uploader = S3Uploader(self.client_provider.s3_client, self.s3_bucket_name) @@ -502,6 +449,13 @@ def verify_stack(self, end_state="CREATE_COMPLETE"): if error: self.fail(error) + @retry( + stop=stop_after_attempt(3), + wait=wait_exponential(multiplier=1, min=4, max=10) + wait_random(0, 1), + retry=retry_if_exception_type(StatusCodeError), + after=after_log(LOG, logging.WARNING), + reraise=True, + ) def verify_get_request_response(self, url, expected_status_code, headers=None): """ Verify if the get request to a certain url return the expected status code @@ -510,13 +464,47 @@ def verify_get_request_response(self, url, expected_status_code, headers=None): ---------- url : string the url for the get request - expected_status_code : string + expected_status_code : int the expected status code headers : dict headers to use in request """ response = BaseTest.do_get_request_with_logging(url, headers) - self.assertEqual(response.status_code, expected_status_code, " must return HTTP " + str(expected_status_code)) + if response.status_code != expected_status_code: + raise StatusCodeError( + "Request to {} failed with status: {}, expected status: {}".format( + url, response.status_code, expected_status_code + ) + ) + return response + + @retry( + stop=stop_after_attempt(3), + wait=wait_exponential(multiplier=1, min=4, max=10) + wait_random(0, 1), + retry=retry_if_exception_type(StatusCodeError), + after=after_log(LOG, logging.WARNING), + reraise=True, + ) + def verify_options_request(self, url, expected_status_code, headers=None): + """ + Verify if the option request to a certain url return the expected status code + + Parameters + ---------- + url : string + the url for the get request + expected_status_code : int + the expected status code + headers : dict + headers to use in request + """ + response = BaseTest.do_options_request_with_logging(url, headers) + if response.status_code != expected_status_code: + raise StatusCodeError( + "Request to {} failed with status: {}, expected status: {}".format( + url, response.status_code, expected_status_code + ) + ) return response def get_default_test_template_parameters(self): @@ -570,3 +558,19 @@ def do_get_request_with_logging(url, headers=None): amazon_headers = RequestUtils(response).get_amazon_headers() REQUEST_LOGGER.info("Request made to " + url, extra={"status": response.status_code, "headers": amazon_headers}) return response + + @staticmethod + def do_options_request_with_logging(url, headers=None): + """ + Perform a options request to an APIGW endpoint and log relevant info + Parameters + ---------- + url : string + the url for the get request + headers : dict + headers to use in request + """ + response = requests.options(url, headers=headers) if headers else requests.options(url) + amazon_headers = RequestUtils(response).get_amazon_headers() + REQUEST_LOGGER.info("Request made to " + url, extra={"status": response.status_code, "headers": amazon_headers}) + return response diff --git a/integration/helpers/common_api.py b/integration/helpers/common_api.py index c261fed5d..d418f6472 100644 --- a/integration/helpers/common_api.py +++ b/integration/helpers/common_api.py @@ -1,6 +1,25 @@ import json +import logging +from tenacity import ( + retry, + stop_after_attempt, + wait_exponential, + retry_if_exception_type, + after_log, + wait_random, +) +LOG = logging.getLogger(__name__) + + +@retry( + stop=stop_after_attempt(3), + wait=wait_exponential(multiplier=1, min=4, max=10) + wait_random(0, 1), + retry=retry_if_exception_type(KeyError), + after=after_log(LOG, logging.WARNING), + reraise=True, +) def get_queue_policy(queue_url, sqs_client): result = sqs_client.get_queue_attributes(QueueUrl=queue_url, AttributeNames=["Policy"]) policy_document = result["Attributes"]["Policy"] diff --git a/integration/helpers/deployer/deployer.py b/integration/helpers/deployer/deployer.py index 63d16cacd..3dd3d3677 100644 --- a/integration/helpers/deployer/deployer.py +++ b/integration/helpers/deployer/deployer.py @@ -26,18 +26,18 @@ # - Moved DeployColor to colors.py # - Removed unnecessary functions from artifact_exporter import sys -import math from collections import OrderedDict import logging import time from datetime import datetime + from integration.helpers.resource import generate_suffix import botocore from integration.helpers.deployer.utils.colors import DeployColor from integration.helpers.deployer.exceptions import exceptions as deploy_exceptions -from integration.helpers.deployer.utils.retry import retry, retry_with_exponential_backoff_and_jitter +from integration.helpers.deployer.utils.retry import retry_with_exponential_backoff_and_jitter from integration.helpers.deployer.utils.table_print import ( pprint_column_names, pprint_columns, @@ -307,75 +307,6 @@ def get_last_event_time(self, stack_name): except KeyError: return time.time() - @pprint_column_names( - format_string=DESCRIBE_STACK_EVENTS_FORMAT_STRING, - format_kwargs=DESCRIBE_STACK_EVENTS_DEFAULT_ARGS, - table_header=DESCRIBE_STACK_EVENTS_TABLE_HEADER_NAME, - ) - def describe_stack_events(self, stack_name, time_stamp_marker, **kwargs): - """ - Calls CloudFormation to get current stack events - :param stack_name: Name or ID of the stack - :param time_stamp_marker: last event time on the stack to start streaming events from. - :return: - """ - - stack_change_in_progress = True - events = set() - retry_attempts = 0 - - while stack_change_in_progress and retry_attempts <= self.max_attempts: - try: - - # Only sleep if there have been no retry_attempts - time.sleep(self.client_sleep if retry_attempts == 0 else 0) - describe_stacks_resp = self._client.describe_stacks(StackName=stack_name) - paginator = self._client.get_paginator("describe_stack_events") - response_iterator = paginator.paginate(StackName=stack_name) - stack_status = describe_stacks_resp["Stacks"][0]["StackStatus"] - latest_time_stamp_marker = time_stamp_marker - for event_items in response_iterator: - for event in event_items["StackEvents"]: - if event["EventId"] not in events and utc_to_timestamp(event["Timestamp"]) > time_stamp_marker: - events.add(event["EventId"]) - latest_time_stamp_marker = max( - latest_time_stamp_marker, utc_to_timestamp(event["Timestamp"]) - ) - row_color = self.deploy_color.get_stack_events_status_color(status=event["ResourceStatus"]) - pprint_columns( - columns=[ - event["ResourceStatus"], - event["ResourceType"], - event["LogicalResourceId"], - event.get("ResourceStatusReason", "-"), - ], - width=kwargs["width"], - margin=kwargs["margin"], - format_string=DESCRIBE_STACK_EVENTS_FORMAT_STRING, - format_args=kwargs["format_args"], - columns_dict=DESCRIBE_STACK_EVENTS_DEFAULT_ARGS.copy(), - color=row_color, - ) - # Skip already shown old event entries - elif utc_to_timestamp(event["Timestamp"]) <= time_stamp_marker: - time_stamp_marker = latest_time_stamp_marker - break - else: # go to next loop if not break from inside loop - time_stamp_marker = latest_time_stamp_marker # update marker if all events are new - continue - break # reached here only if break from inner loop! - - if self._check_stack_complete(stack_status): - stack_change_in_progress = False - break - except botocore.exceptions.ClientError as ex: - retry_attempts = retry_attempts + 1 - if retry_attempts > self.max_attempts: - LOG.error("Describing stack events for %s failed: %s", stack_name, str(ex)) - return - # Sleep in exponential backoff mode - time.sleep(math.pow(self.backoff, retry_attempts)) - def _check_stack_complete(self, status): return "COMPLETE" in status and "CLEANUP" not in status @@ -386,8 +317,6 @@ def wait_for_execute(self, stack_name, changeset_type): ) sys.stdout.flush() - self.describe_stack_events(stack_name, self.get_last_event_time(stack_name)) - # Pick the right waiter if changeset_type == "CREATE": waiter = self._client.get_waiter("stack_create_complete") @@ -443,6 +372,8 @@ def create_and_wait_for_changeset( self.wait_for_changeset(result["Id"], stack_name) self.describe_changeset(result["Id"], stack_name) return result + except deploy_exceptions.ChangeEmptyError as ex: + return {} except botocore.exceptions.ClientError as ex: raise deploy_exceptions.DeployFailedError(stack_name=stack_name, msg=str(ex)) diff --git a/integration/helpers/deployer/exceptions/exceptions.py b/integration/helpers/deployer/exceptions/exceptions.py index 582ef1f74..6522a1fa4 100644 --- a/integration/helpers/deployer/exceptions/exceptions.py +++ b/integration/helpers/deployer/exceptions/exceptions.py @@ -70,6 +70,16 @@ def __init__(self, stack_name, msg): self.stack_name = stack_name self.msg = msg - message_fmt = "Throttling Issue occurred: {stack_name}, {msg}" + message_fmt = "Throttling issue occurred: {stack_name}, {msg}" super(ThrottlingError, self).__init__(message=message_fmt.format(stack_name=self.stack_name, msg=msg)) + + +class S3DoesNotExistException(UserException): + def __init__(self, bucket_name, msg): + self.bucket_name = bucket_name + self.msg = msg + + message_fmt = "Companion S3 bucket used for resource upload does not exist: {bucket_name}, {msg}" + + super(S3DoesNotExistException, self).__init__(message=message_fmt.format(bucket_name=self.bucket_name, msg=msg)) diff --git a/integration/helpers/resource.py b/integration/helpers/resource.py index 704d0f3aa..66c77bfad 100644 --- a/integration/helpers/resource.py +++ b/integration/helpers/resource.py @@ -120,15 +120,17 @@ def create_bucket(bucket_name, region): NoRegionError If region is not specified """ + s3 = boto3.resource("s3") if region is None: raise NoRegionError() + if region == "us-east-1": - s3_client = boto3.client("s3") - s3_client.create_bucket(Bucket=bucket_name) + bucket = s3.create_bucket(Bucket=bucket_name) else: - s3_client = boto3.client("s3", region_name=region) location = {"LocationConstraint": region} - s3_client.create_bucket(Bucket=bucket_name, CreateBucketConfiguration=location) + bucket = s3.create_bucket(Bucket=bucket_name, CreateBucketConfiguration=location) + + bucket.wait_until_exists() def _get_region(): @@ -138,14 +140,24 @@ def _get_region(): return region -def _read_test_config_file(filename): - """Reads test inclusion or exclusion file and returns the contents""" +def read_test_config_file(filename): + """Reads test config file and returns the contents""" tests_integ_dir = Path(__file__).resolve().parents[1] - test_config_file_path = str(Path(tests_integ_dir, "config", filename)) - test_config = load_yaml(test_config_file_path) + test_config_file_path = Path(tests_integ_dir, "config", filename) + if not test_config_file_path.is_file(): + return {} + test_config = load_yaml(str(test_config_file_path)) return test_config +def write_test_config_file_to_json(filename, input): + """Reads test config file and returns the contents""" + tests_integ_dir = Path(__file__).resolve().parents[1] + test_config_file_path = Path(tests_integ_dir, "config", filename) + with open(test_config_file_path, "w") as f: + json.dump(input, f) + + def current_region_does_not_support(services): """ Decide if a test should be skipped in the current testing region with the specific resources @@ -162,9 +174,9 @@ def current_region_does_not_support(services): """ region = _get_region() - region_exclude_services = _read_test_config_file("region_service_exclusion.yaml") + region_exclude_services = read_test_config_file("region_service_exclusion.yaml") - if region not in region_exclude_services["regions"]: + if region not in region_exclude_services.get("regions", {}): return False # check if any one of the services is in the excluded services for current testing region @@ -177,9 +189,9 @@ def current_region_not_included(services): Decides which tests should only be run in certain regions """ region = _get_region() - region_include_services = _read_test_config_file("region_service_inclusion.yaml") + region_include_services = read_test_config_file("region_service_inclusion.yaml") - if region not in region_include_services["regions"]: + if region not in region_include_services.get("regions", {}): return True # check if any one of the services is in the excluded services for current testing region diff --git a/integration/helpers/stack.py b/integration/helpers/stack.py index 1ffafa190..82f28110e 100644 --- a/integration/helpers/stack.py +++ b/integration/helpers/stack.py @@ -22,10 +22,10 @@ def __init__(self, stack_name, template_path, cfn_client, output_dir): self.stack_description = None self.stack_resources = None - def create(self): + def create_or_update(self, update): output_template_path = self._generate_output_file_path(self.template_path, self.output_dir) transform_template(self.template_path, output_template_path) - self._deploy_stack(output_template_path) + self._deploy_stack(output_template_path, update) def delete(self): self.cfn_client.delete_stack(StackName=self.stack_name) @@ -36,7 +36,7 @@ def get_stack_outputs(self): output_list = self.stack_description["Stacks"][0]["Outputs"] return {output["OutputKey"]: output["OutputValue"] for output in output_list} - def _deploy_stack(self, output_file_path, parameters=None): + def _deploy_stack(self, output_file_path, update, parameters=None): """ Deploys the current cloud formation stack """ @@ -50,10 +50,11 @@ def _deploy_stack(self, output_file_path, parameters=None): notification_arns=[], s3_uploader=None, tags=[], - changeset_type="CREATE", + changeset_type="UPDATE" if update else "CREATE", ) - self.deployer.execute_changeset(result["Id"], self.stack_name) - self.deployer.wait_for_execute(self.stack_name, "CREATE") + if result: + self.deployer.execute_changeset(result["Id"], self.stack_name) + self.deployer.wait_for_execute(self.stack_name, "UPDATE" if update else "CREATE") self._get_stack_description() self.stack_resources = self.cfn_client.list_stack_resources(StackName=self.stack_name) diff --git a/integration/resources/expected/combination/api_with_custom_domains_edge.json b/integration/resources/expected/combination/api_with_custom_domains_edge.json index b55752b59..d8dd5918f 100644 --- a/integration/resources/expected/combination/api_with_custom_domains_edge.json +++ b/integration/resources/expected/combination/api_with_custom_domains_edge.json @@ -1,7 +1,7 @@ [ - { "LogicalResourceId":"RecordSetGroup", "ResourceType":"AWS::Route53::RecordSetGroup" }, + { "LogicalResourceId":"RecordSetGroup1b7eeb359e", "ResourceType":"AWS::Route53::RecordSetGroup" }, { "LogicalResourceId":"MyApiDeployment", "ResourceType":"AWS::ApiGateway::Deployment" }, - { "LogicalResourceId":"ApiGatewayDomainName", "ResourceType":"AWS::ApiGateway::DomainName" }, + { "LogicalResourceId":"ApiGatewayDomainName1101a94567", "ResourceType":"AWS::ApiGateway::DomainName" }, { "LogicalResourceId":"MyApi", "ResourceType":"AWS::ApiGateway::RestApi" }, { "LogicalResourceId":"MyApiProdStage", "ResourceType":"AWS::ApiGateway::Stage" }, { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, diff --git a/integration/resources/expected/combination/api_with_custom_domains_regional.json b/integration/resources/expected/combination/api_with_custom_domains_regional.json index 3d6e17b45..b3d611ea3 100644 --- a/integration/resources/expected/combination/api_with_custom_domains_regional.json +++ b/integration/resources/expected/combination/api_with_custom_domains_regional.json @@ -3,10 +3,10 @@ { "LogicalResourceId":"ServerlessRestApiDeployment", "ResourceType":"AWS::ApiGateway::Deployment" }, { "LogicalResourceId":"ServerlessRestApipostBasePathMapping", "ResourceType":"AWS::ApiGateway::BasePathMapping" }, { "LogicalResourceId":"ServerlessRestApi", "ResourceType":"AWS::ApiGateway::RestApi" }, - { "LogicalResourceId":"RecordSetGroup", "ResourceType":"AWS::Route53::RecordSetGroup" }, + { "LogicalResourceId":"RecordSetGroupddfc299be2", "ResourceType":"AWS::Route53::RecordSetGroup" }, { "LogicalResourceId":"ServerlessRestApiProdStage", "ResourceType":"AWS::ApiGateway::Stage" }, { "LogicalResourceId":"ServerlessRestApigetBasePathMapping", "ResourceType":"AWS::ApiGateway::BasePathMapping" }, - { "LogicalResourceId":"ApiGatewayDomainName", "ResourceType":"AWS::ApiGateway::DomainName" }, + { "LogicalResourceId":"ApiGatewayDomainName7898169271", "ResourceType":"AWS::ApiGateway::DomainName" }, { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, { "LogicalResourceId":"MyFunctionImplicitPostPermissionProd", "ResourceType":"AWS::Lambda::Permission" }, { "LogicalResourceId":"MyFunctionRole", "ResourceType":"AWS::IAM::Role" } diff --git a/integration/resources/expected/combination/api_with_custom_domains_regional_ownership_verification.json b/integration/resources/expected/combination/api_with_custom_domains_regional_ownership_verification.json index 3d6e17b45..7b9e37471 100644 --- a/integration/resources/expected/combination/api_with_custom_domains_regional_ownership_verification.json +++ b/integration/resources/expected/combination/api_with_custom_domains_regional_ownership_verification.json @@ -3,10 +3,10 @@ { "LogicalResourceId":"ServerlessRestApiDeployment", "ResourceType":"AWS::ApiGateway::Deployment" }, { "LogicalResourceId":"ServerlessRestApipostBasePathMapping", "ResourceType":"AWS::ApiGateway::BasePathMapping" }, { "LogicalResourceId":"ServerlessRestApi", "ResourceType":"AWS::ApiGateway::RestApi" }, - { "LogicalResourceId":"RecordSetGroup", "ResourceType":"AWS::Route53::RecordSetGroup" }, + { "LogicalResourceId":"RecordSetGroupddfc299be2", "ResourceType":"AWS::Route53::RecordSetGroup" }, { "LogicalResourceId":"ServerlessRestApiProdStage", "ResourceType":"AWS::ApiGateway::Stage" }, { "LogicalResourceId":"ServerlessRestApigetBasePathMapping", "ResourceType":"AWS::ApiGateway::BasePathMapping" }, - { "LogicalResourceId":"ApiGatewayDomainName", "ResourceType":"AWS::ApiGateway::DomainName" }, + { "LogicalResourceId":"ApiGatewayDomainNamec6418178e6", "ResourceType":"AWS::ApiGateway::DomainName" }, { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, { "LogicalResourceId":"MyFunctionImplicitPostPermissionProd", "ResourceType":"AWS::Lambda::Permission" }, { "LogicalResourceId":"MyFunctionRole", "ResourceType":"AWS::IAM::Role" } diff --git a/integration/resources/expected/combination/api_with_fail_on_warnings.json b/integration/resources/expected/combination/api_with_fail_on_warnings.json new file mode 100644 index 000000000..b1f81c7a0 --- /dev/null +++ b/integration/resources/expected/combination/api_with_fail_on_warnings.json @@ -0,0 +1,8 @@ +[ + {"LogicalResourceId": "RestApiGateway", "ResourceType": "AWS::ApiGateway::RestApi"}, + {"LogicalResourceId": "RestApiGatewayDeployment", "ResourceType": "AWS::ApiGateway::Deployment"}, + {"LogicalResourceId": "RestApiGatewayProdStage", "ResourceType": "AWS::ApiGateway::Stage"}, + {"LogicalResourceId": "RestApiFunction", "ResourceType": "AWS::Lambda::Function"}, + {"LogicalResourceId": "RestApiFunctionIamPermissionProd", "ResourceType": "AWS::Lambda::Permission"}, + {"LogicalResourceId": "RestApiFunctionRole", "ResourceType": "AWS::IAM::Role"} +] \ No newline at end of file diff --git a/integration/resources/expected/combination/function_with_file_system_config.json b/integration/resources/expected/combination/function_with_file_system_config.json index 5f6e3fe3d..6d5b273af 100644 --- a/integration/resources/expected/combination/function_with_file_system_config.json +++ b/integration/resources/expected/combination/function_with_file_system_config.json @@ -3,8 +3,6 @@ { "LogicalResourceId":"MountTarget", "ResourceType":"AWS::EFS::MountTarget" }, { "LogicalResourceId":"AccessPoint", "ResourceType":"AWS::EFS::AccessPoint" }, { "LogicalResourceId":"LambdaFunctionWithEfs", "ResourceType":"AWS::Lambda::Function" }, - { "LogicalResourceId":"MyVpc", "ResourceType":"AWS::EC2::VPC" }, { "LogicalResourceId":"MySecurityGroup", "ResourceType":"AWS::EC2::SecurityGroup" }, - { "LogicalResourceId":"MySubnet", "ResourceType":"AWS::EC2::Subnet" }, { "LogicalResourceId":"LambdaFunctionWithEfsRole", "ResourceType":"AWS::IAM::Role" } ] \ No newline at end of file diff --git a/integration/resources/expected/combination/http_api_with_custom_domains_regional_ownership_verification.json b/integration/resources/expected/combination/http_api_with_custom_domains_regional_ownership_verification.json index 7c7db8ba4..cedc71649 100644 --- a/integration/resources/expected/combination/http_api_with_custom_domains_regional_ownership_verification.json +++ b/integration/resources/expected/combination/http_api_with_custom_domains_regional_ownership_verification.json @@ -6,7 +6,7 @@ { "LogicalResourceId":"MyApi", "ResourceType":"AWS::ApiGatewayV2::Api" }, { "LogicalResourceId":"RecordSetGroupddfc299be2", "ResourceType":"AWS::Route53::RecordSetGroup" }, { "LogicalResourceId":"MyApiProdStage", "ResourceType":"AWS::ApiGatewayV2::Stage" }, - { "LogicalResourceId":"ApiGatewayDomainNameV2e7a0af471b", "ResourceType":"AWS::ApiGatewayV2::DomainName" }, + { "LogicalResourceId":"ApiGatewayDomainNameV2804ee5e222", "ResourceType":"AWS::ApiGatewayV2::DomainName" }, { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, { "LogicalResourceId":"MyFunctionRole", "ResourceType":"AWS::IAM::Role" } ] \ No newline at end of file diff --git a/integration/resources/expected/combination/intrinsics_serverless_function.json b/integration/resources/expected/combination/intrinsics_serverless_function.json index 22203a11f..ef8334560 100644 --- a/integration/resources/expected/combination/intrinsics_serverless_function.json +++ b/integration/resources/expected/combination/intrinsics_serverless_function.json @@ -1,7 +1,5 @@ [ { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, { "LogicalResourceId":"MyNewRole", "ResourceType":"AWS::IAM::Role" }, - { "LogicalResourceId":"MyVpc", "ResourceType":"AWS::EC2::VPC" }, - { "LogicalResourceId":"MySecurityGroup", "ResourceType":"AWS::EC2::SecurityGroup" }, - { "LogicalResourceId":"MySubnet", "ResourceType":"AWS::EC2::Subnet" } + { "LogicalResourceId":"MySecurityGroup", "ResourceType":"AWS::EC2::SecurityGroup" } ] \ No newline at end of file diff --git a/integration/resources/expected/single/basic_api_with_tags.json b/integration/resources/expected/single/basic_api_with_tags.json index d636093ca..fde444738 100644 --- a/integration/resources/expected/single/basic_api_with_tags.json +++ b/integration/resources/expected/single/basic_api_with_tags.json @@ -1,5 +1,5 @@ [ { "LogicalResourceId":"MyApi", "ResourceType":"AWS::ApiGateway::RestApi" }, { "LogicalResourceId":"MyApiDeployment", "ResourceType":"AWS::ApiGateway::Deployment" }, - { "LogicalResourceId":"MyApiStage", "ResourceType":"AWS::ApiGateway::Stage" } + { "LogicalResourceId":"MyApiStage00dadc1ecc", "ResourceType":"AWS::ApiGateway::Stage" } ] \ No newline at end of file diff --git a/integration/resources/templates/combination/api_with_custom_domains_regional_ownership_verification.yaml b/integration/resources/templates/combination/api_with_custom_domains_regional_ownership_verification.yaml index 7c1fe5498..c95b23f1f 100644 --- a/integration/resources/templates/combination/api_with_custom_domains_regional_ownership_verification.yaml +++ b/integration/resources/templates/combination/api_with_custom_domains_regional_ownership_verification.yaml @@ -12,7 +12,7 @@ Globals: DomainName: Ref: MyDomainName CertificateArn: - arn:aws:acm:us-east-1:830899278857:certificate/7c9b4aa7-4262-4b20-85c0-ec45d8d55fa6 + Ref: MyDomainCertificate EndpointConfiguration: REGIONAL MutualTlsAuthentication: TruststoreUri: ${mtlsuri} @@ -24,7 +24,7 @@ Globals: Route53: HostedZoneId: Z1DTV8GVAVOHDR OwnershipVerificationCertificateArn: - Fn::Sub: arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/798c74e5-dc55-48b6-b1d8-13d2d7b3d265 + Ref: MyDomainOwnershipVerificationCertificate Resources: MyFunction: diff --git a/integration/resources/templates/combination/api_with_fail_on_warnings.yaml b/integration/resources/templates/combination/api_with_fail_on_warnings.yaml new file mode 100644 index 000000000..f0a086ed8 --- /dev/null +++ b/integration/resources/templates/combination/api_with_fail_on_warnings.yaml @@ -0,0 +1,34 @@ +Parameters: + FailOnWarningsValue: + Type: String + AllowedValues: [true, false] + +Resources: + RestApiGateway: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + FailOnWarnings: + Ref: FailOnWarningsValue + + RestApiFunction: + Type: AWS::Serverless::Function + Properties: + InlineCode: | + exports.handler = async (event) => { + const response = { + statusCode: 200, + body: JSON.stringify('Hello from Lambda!'), + }; + return response; + }; + Handler: index.handler + Runtime: nodejs12.x + Events: + Iam: + Type: Api + Properties: + RestApiId: + Ref: RestApiGateway + Method: GET + Path: / diff --git a/integration/resources/templates/combination/function_with_file_system_config.yaml b/integration/resources/templates/combination/function_with_file_system_config.yaml index c1d257862..8529d53ad 100644 --- a/integration/resources/templates/combination/function_with_file_system_config.yaml +++ b/integration/resources/templates/combination/function_with_file_system_config.yaml @@ -1,5 +1,11 @@ Description: SAM + Lambda + EFS +Parameters: + PreCreatedSubnetOne: + Type: String + PreCreatedVpc: + Type: String + Resources: EfsFileSystem: Type: AWS::EFS::FileSystem @@ -10,7 +16,7 @@ Resources: FileSystemId: Ref: EfsFileSystem SubnetId: - Ref: MySubnet + Ref: PreCreatedSubnetOne SecurityGroups: - Fn::GetAtt: MySecurityGroup.GroupId @@ -45,27 +51,15 @@ Resources: Fn::GetAtt: MySecurityGroup.GroupId SubnetIds: - - Ref: MySubnet + Ref: PreCreatedSubnetOne FileSystemConfigs: - Arn: Fn::GetAtt: AccessPoint.Arn LocalMountPath: /mnt/EFS - MyVpc: - Type: "AWS::EC2::VPC" - Properties: - CidrBlock: "10.0.0.0/16" - MySecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: GroupDescription: "my test group" VpcId: - Ref: MyVpc - - MySubnet: - Type: "AWS::EC2::Subnet" - Properties: - VpcId: - Ref: MyVpc - CidrBlock: "10.0.0.0/24" + Ref: PreCreatedVpc diff --git a/integration/resources/templates/combination/intrinsics_serverless_function.yaml b/integration/resources/templates/combination/intrinsics_serverless_function.yaml index 2ddb10d4f..703242bcc 100644 --- a/integration/resources/templates/combination/intrinsics_serverless_function.yaml +++ b/integration/resources/templates/combination/intrinsics_serverless_function.yaml @@ -14,6 +14,10 @@ Parameters: AutoPublishSha: Type: String Default: AnyRandomStringWillActuallyDo + PreCreatedSubnetOne: + Type: String + PreCreatedVpc: + Type: String Conditions: TrueCondition: @@ -68,7 +72,7 @@ Resources: SecurityGroupIds: - "Fn::GetAtt": ["MySecurityGroup", "GroupId"] SubnetIds: - - Ref: "MySubnet" + - Ref: PreCreatedSubnetOne # Additional resources to reference inside the Function resource MyNewRole: @@ -87,25 +91,12 @@ Resources: Service: - lambda.amazonaws.com - - MyVpc: - Type: "AWS::EC2::VPC" - Properties: - CidrBlock: "10.0.0.0/16" - MySecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: GroupDescription: "my test group" VpcId: - Ref: MyVpc - - MySubnet: - Type: "AWS::EC2::Subnet" - Properties: - VpcId: - Ref: MyVpc - CidrBlock: "10.0.0.0/24" + Ref: PreCreatedVpc # False condition, shouldn't be created MyFunctionFalseCondition: diff --git a/integration/setup/companion-stack.yaml b/integration/setup/companion-stack.yaml index 35521d7f2..e33fc1d7d 100644 --- a/integration/setup/companion-stack.yaml +++ b/integration/setup/companion-stack.yaml @@ -37,6 +37,10 @@ Resources: InternetGatewayId: Ref: PreCreatedInternetGateway + PreCreatedS3Bucket: + Type: AWS::S3::Bucket + DeletionPolicy: Delete + Outputs: PreCreatedVpc: Description: "Pre-created VPC that can be used inside other tests" @@ -57,4 +61,8 @@ Outputs: PreCreatedAttachGateway: Description: "Pre-created Attach Gateway that can be used inside other tests" Value: - Ref: PreCreatedAttachGateway \ No newline at end of file + Ref: PreCreatedAttachGateway + PreCreatedS3Bucket: + Description: "Pre-created S3 Bucket that can be used inside other tests" + Value: + Ref: PreCreatedS3Bucket \ No newline at end of file diff --git a/integration/setup/test_setup_teardown.py b/integration/setup/test_setup_teardown.py index 49ba6aa96..35d2d1be7 100644 --- a/integration/setup/test_setup_teardown.py +++ b/integration/setup/test_setup_teardown.py @@ -1,11 +1,20 @@ import pytest +from integration.helpers.resource import read_test_config_file @pytest.mark.setup -def test_setup(setup_companion_stack_once): - assert True +def test_setup(setup_companion_stack_once, upload_resources, get_s3): + assert s3_upload_successful() @pytest.mark.teardown def test_teardown(delete_companion_stack_once): assert True + + +def s3_upload_successful(): + modified_map = read_test_config_file("file_to_s3_map_modified.json") + for _, file_info in modified_map.items(): + if not file_info["uri"]: + return False + return True diff --git a/integration/single/test_basic_api.py b/integration/single/test_basic_api.py index 402fd054f..72b2f1394 100644 --- a/integration/single/test_basic_api.py +++ b/integration/single/test_basic_api.py @@ -1,12 +1,18 @@ -import time +import logging from unittest.case import skipIf +from tenacity import stop_after_attempt, wait_exponential, retry_if_exception_type, after_log, wait_random + from integration.helpers.base_test import BaseTest +from integration.helpers.exception import StatusCodeError from integration.helpers.resource import current_region_does_not_support -from integration.config.service_names import MODE +from integration.config.service_names import MODE, REST_API + +LOG = logging.getLogger(__name__) +@skipIf(current_region_does_not_support([REST_API]), "Rest API is not supported in this testing region") class TestBasicApi(BaseTest): """ Basic AWS::Serverless::Api tests @@ -39,22 +45,22 @@ def test_basic_api_with_mode(self): stack_output = self.get_stack_outputs() api_endpoint = stack_output.get("ApiEndpoint") - response = BaseTest.do_get_request_with_logging(f"{api_endpoint}/get") - self.assertEqual(response.status_code, 200) + + self.verify_get_request_response(f"{api_endpoint}/get", 200) # Removes get from the API self.update_and_verify_stack(file_path="single/basic_api_with_mode_update") - response = BaseTest.do_get_request_with_logging(f"{api_endpoint}/get") + # API Gateway by default returns 403 if a path do not exist - retries = 20 - while retries > 0: - retries -= 1 - response = BaseTest.do_get_request_with_logging(f"{api_endpoint}/get") - if response.status_code != 500: - break - time.sleep(5) - - self.assertEqual(response.status_code, 403) + self.verify_get_request_response.retry_with( + stop=stop_after_attempt(20), + wait=wait_exponential(multiplier=1, min=4, max=10) + wait_random(0, 1), + retry=retry_if_exception_type(StatusCodeError), + after=after_log(LOG, logging.WARNING), + reraise=True, + )(self, f"{api_endpoint}/get", 403) + + LOG.log(msg=f"retry times {self.verify_get_request_response.retry.statistics}", level=logging.WARNING) def test_basic_api_inline_openapi(self): """ diff --git a/integration/single/test_basic_function.py b/integration/single/test_basic_function.py index 0c4ea07d2..a80226c0f 100644 --- a/integration/single/test_basic_function.py +++ b/integration/single/test_basic_function.py @@ -1,10 +1,15 @@ +import logging from unittest.case import skipIf -from integration.config.service_names import KMS, XRAY, ARM +import pytest + +from integration.config.service_names import KMS, XRAY, ARM, CODE_DEPLOY, HTTP_API from integration.helpers.resource import current_region_does_not_support from parameterized import parameterized from integration.helpers.base_test import BaseTest +LOG = logging.getLogger(__name__) + class TestBasicFunction(BaseTest): """ @@ -35,12 +40,14 @@ def test_basic_function(self, file_name): "single/function_alias_with_http_api_events", ] ) + @pytest.mark.flaky(reruns=5) + @skipIf(current_region_does_not_support([HTTP_API]), "HTTP API is not supported in this testing region") def test_function_with_http_api_events(self, file_name): self.create_and_verify_stack(file_name) endpoint = self.get_api_v2_endpoint("MyHttpApi") - self.assertEqual(BaseTest.do_get_request_with_logging(endpoint).text, self.FUNCTION_OUTPUT) + self._verify_get_request(endpoint, self.FUNCTION_OUTPUT) @parameterized.expand( [ @@ -98,6 +105,7 @@ def test_basic_function_with_url_config(self, file_name, qualifier): self.assertEqual(function_url_config["Cors"], cors_config) self._assert_invoke(lambda_client, function_name, qualifier, 200) + @skipIf(current_region_does_not_support([CODE_DEPLOY]), "CodeDeploy is not supported in this testing region") def test_function_with_deployment_preference_alarms_intrinsic_if(self): self.create_and_verify_stack("single/function_with_deployment_preference_alarms_intrinsic_if") @@ -280,3 +288,7 @@ def _assert_invoke(self, lambda_client, function_name, qualifier=None, expected_ response = lambda_client.invoke(**request_params) self.assertEqual(response.get("StatusCode"), expected_status_code) + + def _verify_get_request(self, url, expected_text): + response = self.verify_get_request_response(url, 200) + self.assertEqual(response.text, expected_text) diff --git a/integration/single/test_function_with_http_api_and_auth.py b/integration/single/test_function_with_http_api_and_auth.py index 02cd368bb..22b9baef5 100644 --- a/integration/single/test_function_with_http_api_and_auth.py +++ b/integration/single/test_function_with_http_api_and_auth.py @@ -11,7 +11,7 @@ def test_function_with_http_api_and_auth(self): # We are not testing that IAM auth works here, we are simply testing if it was applied. IAM_AUTH_OUTPUT = '{"message":"Forbidden"}' - self.create_and_verify_stack("function_with_http_api_events_and_auth") + self.create_and_verify_stack("single/function_with_http_api_events_and_auth") implicitEndpoint = self.get_api_v2_endpoint("ServerlessHttpApi") self.assertEqual( diff --git a/requirements/dev.txt b/requirements/dev.txt index 1c674a098..95f8dfd65 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -15,6 +15,7 @@ parameterized~=0.7.4 click~=7.1 dateparser~=0.7 boto3>=1.23,<2 +tenacity~=7.0.0 # Requirements for examples requests~=2.24.0 diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index 9a9a7e961..9730661aa 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.47.0" +__version__ = "1.48.0" diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 92b6b9fb6..304cbdcb0 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -22,7 +22,7 @@ from samtranslator.swagger.swagger import SwaggerEditor from samtranslator.model.intrinsics import is_intrinsic, fnSub from samtranslator.model.lambda_ import LambdaPermission -from samtranslator.translator import logical_id_generator +from samtranslator.translator.logical_id_generator import LogicalIdGenerator from samtranslator.translator.arn_generator import ArnGenerator from samtranslator.model.tags.resource_tagging import get_tag_list from samtranslator.utils.py27hash_fix import Py27Dict, Py27UniStr @@ -181,6 +181,7 @@ def __init__( open_api_version=None, models=None, domain=None, + fail_on_warnings=None, description=None, mode=None, api_key_source_type=None, @@ -232,6 +233,7 @@ def __init__( self.remove_extra_stage = open_api_version self.models = models self.domain = domain + self.fail_on_warnings = fail_on_warnings self.description = description self.shared_api_usage_plan = shared_api_usage_plan self.template_conditions = template_conditions @@ -277,6 +279,9 @@ def _construct_rest_api(self): self._add_binary_media_types() self._add_models() + if self.fail_on_warnings: + rest_api.FailOnWarnings = self.fail_on_warnings + if self.disable_execute_api_endpoint is not None: self._add_endpoint_extension() @@ -388,7 +393,7 @@ def _construct_stage(self, deployment, swagger, redeploy_restapi_parameters): if stage_name_prefix.isalnum(): stage_logical_id = self.logical_id + stage_name_prefix + "Stage" else: - generator = logical_id_generator.LogicalIdGenerator(self.logical_id + "Stage", stage_name_prefix) + generator = LogicalIdGenerator(self.logical_id + "Stage", stage_name_prefix) stage_logical_id = generator.gen() stage = ApiGatewayStage(stage_logical_id, attributes=self.passthrough_resource_attributes) stage.RestApiId = ref(self.logical_id) @@ -412,7 +417,7 @@ def _construct_stage(self, deployment, swagger, redeploy_restapi_parameters): return stage - def _construct_api_domain(self, rest_api): + def _construct_api_domain(self, rest_api, route53_record_set_groups): """ Constructs and returns the ApiGateway Domain and BasepathMapping """ @@ -425,7 +430,7 @@ def _construct_api_domain(self, rest_api): ) self.domain["ApiDomainName"] = "{}{}".format( - "ApiGatewayDomainName", logical_id_generator.LogicalIdGenerator("", self.domain.get("DomainName")).gen() + "ApiGatewayDomainName", LogicalIdGenerator("", self.domain.get("DomainName")).gen() ) domain = ApiGatewayDomainName(self.domain.get("ApiDomainName"), attributes=self.passthrough_resource_attributes) @@ -529,17 +534,23 @@ def _construct_api_domain(self, rest_api): self.logical_id, "HostedZoneId or HostedZoneName is required to enable Route53 support on Custom Domains.", ) - logical_id = logical_id_generator.LogicalIdGenerator( + + logical_id_suffix = LogicalIdGenerator( "", route53.get("HostedZoneId") or route53.get("HostedZoneName") ).gen() - record_set_group = Route53RecordSetGroup( - "RecordSetGroup" + logical_id, attributes=self.passthrough_resource_attributes - ) - if "HostedZoneId" in route53: - record_set_group.HostedZoneId = route53.get("HostedZoneId") - if "HostedZoneName" in route53: - record_set_group.HostedZoneName = route53.get("HostedZoneName") - record_set_group.RecordSets = self._construct_record_sets_for_domain(self.domain) + logical_id = "RecordSetGroup" + logical_id_suffix + + record_set_group = route53_record_set_groups.get(logical_id) + if not record_set_group: + record_set_group = Route53RecordSetGroup(logical_id, attributes=self.passthrough_resource_attributes) + if "HostedZoneId" in route53: + record_set_group.HostedZoneId = route53.get("HostedZoneId") + if "HostedZoneName" in route53: + record_set_group.HostedZoneName = route53.get("HostedZoneName") + record_set_group.RecordSets = [] + route53_record_set_groups[logical_id] = record_set_group + + record_set_group.RecordSets += self._construct_record_sets_for_domain(self.domain) return domain, basepath_resource_list, record_set_group @@ -580,14 +591,14 @@ def _construct_alias_target(self, domain): return alias_target @cw_timer(prefix="Generator", name="Api") - def to_cloudformation(self, redeploy_restapi_parameters): + def to_cloudformation(self, redeploy_restapi_parameters, route53_record_set_groups): """Generates CloudFormation resources from a SAM API resource :returns: a tuple containing the RestApi, Deployment, and Stage for an empty Api. :rtype: tuple """ rest_api = self._construct_rest_api() - domain, basepath_mapping, route53 = self._construct_api_domain(rest_api) + domain, basepath_mapping, route53 = self._construct_api_domain(rest_api, route53_record_set_groups) deployment = self._construct_deployment(rest_api) swagger = None diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py index 869dddec4..ddba5d277 100644 --- a/samtranslator/model/api/http_api_generator.py +++ b/samtranslator/model/api/http_api_generator.py @@ -13,7 +13,7 @@ from samtranslator.model.exceptions import InvalidResourceException from samtranslator.model.s3_utils.uri_parser import parse_s3_uri from samtranslator.open_api.open_api import OpenApiEditor -from samtranslator.translator import logical_id_generator +from samtranslator.translator.logical_id_generator import LogicalIdGenerator from samtranslator.model.tags.resource_tagging import get_tag_list from samtranslator.model.intrinsics import is_intrinsic, is_intrinsic_no_value from samtranslator.model.route53 import Route53RecordSetGroup @@ -48,7 +48,7 @@ def __init__( resource_attributes=None, passthrough_resource_attributes=None, domain=None, - fail_on_warnings=False, + fail_on_warnings=None, description=None, disable_execute_api_endpoint=None, ): @@ -218,7 +218,7 @@ def _add_cors(self): # Assign the OpenApi back to template self.definition_body = editor.openapi - def _construct_api_domain(self, http_api): + def _construct_api_domain(self, http_api, route53_record_set_groups): """ Constructs and returns the ApiGateway Domain and BasepathMapping """ @@ -231,7 +231,7 @@ def _construct_api_domain(self, http_api): ) self.domain["ApiDomainName"] = "{}{}".format( - "ApiGatewayDomainNameV2", logical_id_generator.LogicalIdGenerator("", self.domain.get("DomainName")).gen() + "ApiGatewayDomainNameV2", LogicalIdGenerator("", self.domain.get("DomainName")).gen() ) domain = ApiGatewayV2DomainName( @@ -302,31 +302,40 @@ def _construct_api_domain(self, http_api): basepath_resource_list = self._construct_basepath_mappings(basepaths, http_api) # Create the Route53 RecordSetGroup resource - record_set_group = self._construct_route53_recordsetgroup() + record_set_group = self._construct_route53_recordsetgroup(route53_record_set_groups) return domain, basepath_resource_list, record_set_group - def _construct_route53_recordsetgroup(self): - record_set_group = None - if self.domain.get("Route53") is not None: - route53 = self.domain.get("Route53") - if route53.get("HostedZoneId") is None and route53.get("HostedZoneName") is None: - raise InvalidResourceException( - self.logical_id, - "HostedZoneId or HostedZoneName is required to enable Route53 support on Custom Domains.", - ) - logical_id = logical_id_generator.LogicalIdGenerator( - "", route53.get("HostedZoneId") or route53.get("HostedZoneName") - ).gen() - record_set_group = Route53RecordSetGroup( - "RecordSetGroup" + logical_id, attributes=self.passthrough_resource_attributes + def _construct_route53_recordsetgroup(self, route53_record_set_groups): + if self.domain.get("Route53") is None: + return + route53 = self.domain.get("Route53") + if not isinstance(route53, dict): + raise InvalidResourceException( + self.logical_id, + "Invalid property type '{}' for Route53. " + "Expected a map defines an Amazon Route 53 configuration'.".format(type(route53).__name__), ) + if route53.get("HostedZoneId") is None and route53.get("HostedZoneName") is None: + raise InvalidResourceException( + self.logical_id, + "HostedZoneId or HostedZoneName is required to enable Route53 support on Custom Domains.", + ) + + logical_id_suffix = LogicalIdGenerator("", route53.get("HostedZoneId") or route53.get("HostedZoneName")).gen() + logical_id = "RecordSetGroup" + logical_id_suffix + + record_set_group = route53_record_set_groups.get(logical_id) + if not record_set_group: + record_set_group = Route53RecordSetGroup(logical_id, attributes=self.passthrough_resource_attributes) if "HostedZoneId" in route53: record_set_group.HostedZoneId = route53.get("HostedZoneId") elif "HostedZoneName" in route53: record_set_group.HostedZoneName = route53.get("HostedZoneName") - record_set_group.RecordSets = self._construct_record_sets_for_domain(self.domain) + record_set_group.RecordSets = [] + route53_record_set_groups[logical_id] = record_set_group + record_set_group.RecordSets += self._construct_record_sets_for_domain(self.domain) return record_set_group def _construct_basepath_mappings(self, basepaths, http_api): @@ -592,7 +601,7 @@ def _construct_stage(self): elif stage_name_prefix == DefaultStageName: stage_logical_id = self.logical_id + "ApiGatewayDefaultStage" else: - generator = logical_id_generator.LogicalIdGenerator(self.logical_id + "Stage", stage_name_prefix) + generator = LogicalIdGenerator(self.logical_id + "Stage", stage_name_prefix) stage_logical_id = generator.gen() stage = ApiGatewayV2Stage(stage_logical_id, attributes=self.passthrough_resource_attributes) stage.ApiId = ref(self.logical_id) @@ -628,14 +637,14 @@ def _add_description(self): self.definition_body = open_api_editor.openapi @cw_timer(prefix="Generator", name="HttpApi") - def to_cloudformation(self): + def to_cloudformation(self, route53_record_set_groups): """Generates CloudFormation resources from a SAM HTTP API resource :returns: a tuple containing the HttpApi and Stage for an empty Api. :rtype: tuple """ http_api = self._construct_http_api() - domain, basepath_mapping, route53 = self._construct_api_domain(http_api) + domain, basepath_mapping, route53 = self._construct_api_domain(http_api, route53_record_set_groups) stage = self._construct_stage() return http_api, stage, domain, basepath_mapping, route53 diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index ce4ffa3e9..deee74c21 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -1073,6 +1073,7 @@ class SamApi(SamResourceMacro): "OpenApiVersion": PropertyType(False, is_str()), "Models": PropertyType(False, is_type(dict)), "Domain": PropertyType(False, is_type(dict)), + "FailOnWarnings": PropertyType(False, is_type(bool)), "Description": PropertyType(False, is_str()), "Mode": PropertyType(False, is_str()), "DisableExecuteApiEndpoint": PropertyType(False, is_type(bool)), @@ -1106,6 +1107,7 @@ def to_cloudformation(self, **kwargs): redeploy_restapi_parameters = kwargs.get("redeploy_restapi_parameters") shared_api_usage_plan = kwargs.get("shared_api_usage_plan") template_conditions = kwargs.get("conditions") + route53_record_set_groups = kwargs.get("route53_record_set_groups", {}) api_generator = ApiGenerator( self.logical_id, @@ -1136,6 +1138,7 @@ def to_cloudformation(self, **kwargs): open_api_version=self.OpenApiVersion, models=self.Models, domain=self.Domain, + fail_on_warnings=self.FailOnWarnings, description=self.Description, mode=self.Mode, api_key_source_type=self.ApiKeySourceType, @@ -1150,7 +1153,7 @@ def to_cloudformation(self, **kwargs): basepath_mapping, route53, usage_plan_resources, - ) = api_generator.to_cloudformation(redeploy_restapi_parameters) + ) = api_generator.to_cloudformation(redeploy_restapi_parameters, route53_record_set_groups) resources.extend([rest_api, deployment, stage]) resources.extend(permissions) @@ -1210,8 +1213,6 @@ def to_cloudformation(self, **kwargs): resources = [] intrinsics_resolver = kwargs["intrinsics_resolver"] self.CorsConfiguration = intrinsics_resolver.resolve_parameter_refs(self.CorsConfiguration) - - intrinsics_resolver = kwargs["intrinsics_resolver"] self.Domain = intrinsics_resolver.resolve_parameter_refs(self.Domain) api_generator = HttpApiGenerator( @@ -1241,7 +1242,7 @@ def to_cloudformation(self, **kwargs): domain, basepath_mapping, route53, - ) = api_generator.to_cloudformation() + ) = api_generator.to_cloudformation(kwargs.get("route53_record_set_groups", {})) resources.append(http_api) if domain: diff --git a/samtranslator/translator/translator.py b/samtranslator/translator/translator.py index e4c65f4b0..a059d378d 100644 --- a/samtranslator/translator/translator.py +++ b/samtranslator/translator/translator.py @@ -125,6 +125,7 @@ def translate(self, sam_template, parameter_values, feature_toggle=None, passthr shared_api_usage_plan = SharedApiUsagePlan() document_errors = [] changed_logical_ids = {} + route53_record_set_groups = {} for logical_id, resource_dict in self._get_resources_to_iterate(sam_template, macro_resolver): try: macro = macro_resolver.resolve_resource_type(resource_dict).from_dict( @@ -144,6 +145,7 @@ def translate(self, sam_template, parameter_values, feature_toggle=None, passthr kwargs["redeploy_restapi_parameters"] = self.redeploy_restapi_parameters kwargs["shared_api_usage_plan"] = shared_api_usage_plan kwargs["feature_toggle"] = self.feature_toggle + kwargs["route53_record_set_groups"] = route53_record_set_groups translated = macro.to_cloudformation(**kwargs) supported_resource_refs = macro.get_resource_references(translated, supported_resource_refs) diff --git a/samtranslator/validator/sam_schema/definitions/api.json b/samtranslator/validator/sam_schema/definitions/api.json index 10028d888..ba1da1584 100644 --- a/samtranslator/validator/sam_schema/definitions/api.json +++ b/samtranslator/validator/sam_schema/definitions/api.json @@ -117,6 +117,12 @@ "EndpointConfiguration": { "$ref": "#definitions/AWS::Serverless::Api.EndpointConfiguration" }, + "FailOnWarnings": { + "type": [ + "boolean", + "intrinsic" + ] + }, "GatewayResponses": { "$ref": "common.json#definitions/GatewayResponses", "references": [ diff --git a/tests/model/api/test_http_api_generator.py b/tests/model/api/test_http_api_generator.py index 54f9e287f..007952421 100644 --- a/tests/model/api/test_http_api_generator.py +++ b/tests/model/api/test_http_api_generator.py @@ -216,11 +216,14 @@ class TestCustomDomains(TestCase): "passthrough_resource_attributes": None, "domain": None, } + route53_record_set_groups = {} def test_no_domain(self): self.kwargs["domain"] = None http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() - domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain( + http_api, self.route53_record_set_groups + ) self.assertIsNone(domain) self.assertIsNone(basepath) self.assertIsNone(route) @@ -229,7 +232,7 @@ def test_no_domain_name(self): self.kwargs["domain"] = {"CertificateArn": "someurl"} http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() with pytest.raises(InvalidResourceException) as e: - HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api, self.route53_record_set_groups) self.assertEqual( e.value.message, "Resource with id [HttpApiId] is invalid. " @@ -240,7 +243,7 @@ def test_no_cert_arn(self): self.kwargs["domain"] = {"DomainName": "example.com"} http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() with pytest.raises(InvalidResourceException) as e: - HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api, self.route53_record_set_groups) self.assertEqual( e.value.message, "Resource with id [HttpApiId] is invalid. " @@ -250,7 +253,9 @@ def test_no_cert_arn(self): def test_basic_domain_default_endpoint(self): self.kwargs["domain"] = {"DomainName": "example.com", "CertificateArn": "some-url"} http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() - domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain( + http_api, self.route53_record_set_groups + ) self.assertIsNotNone(domain, None) self.assertIsNotNone(basepath, None) self.assertEqual(len(basepath), 1) @@ -264,7 +269,9 @@ def test_basic_domain_regional_endpoint(self): "EndpointConfiguration": "REGIONAL", } http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() - domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain( + http_api, self.route53_record_set_groups + ) self.assertIsNotNone(domain, None) self.assertIsNotNone(basepath, None) self.assertEqual(len(basepath), 1) @@ -279,7 +286,7 @@ def test_basic_domain_edge_endpoint(self): } http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() with pytest.raises(InvalidResourceException) as e: - HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api, self.route53_record_set_groups) self.assertEqual( e.value.message, "Resource with id [HttpApiId] is invalid. EndpointConfiguration for Custom Domains must be one of ['REGIONAL'].", @@ -293,7 +300,7 @@ def test_bad_endpoint(self): } http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() with pytest.raises(InvalidResourceException) as e: - HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api, self.route53_record_set_groups) self.assertEqual( e.value.message, "Resource with id [HttpApiId] is invalid. " @@ -307,7 +314,9 @@ def test_basic_route53(self): "Route53": {"HostedZoneId": "xyz"}, } http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() - domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain( + http_api, self.route53_record_set_groups + ) self.assertIsNotNone(domain, None) self.assertIsNotNone(basepath, None) self.assertEqual(len(basepath), 1) @@ -322,7 +331,9 @@ def test_basepaths(self): "Route53": {"HostedZoneId": "xyz"}, } http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() - domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain( + http_api, self.route53_record_set_groups + ) self.assertIsNotNone(domain, None) self.assertIsNotNone(basepath, None) self.assertEqual(len(basepath), 3) @@ -338,7 +349,7 @@ def test_invalid_basepaths(self): } http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() with pytest.raises(InvalidResourceException) as e: - HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api, self.route53_record_set_groups) self.assertEqual( e.value.message, "Resource with id [HttpApiId] is invalid. " + "Invalid Basepath name provided." ) @@ -351,7 +362,9 @@ def test_basepaths(self): "Route53": {"HostedZoneId": "xyz", "HostedZoneName": "abc", "IpV6": True}, } http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() - domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain(http_api) + domain, basepath, route = HttpApiGenerator(**self.kwargs)._construct_api_domain( + http_api, self.route53_record_set_groups + ) self.assertIsNotNone(domain, None) self.assertIsNotNone(basepath, None) self.assertEqual(len(basepath), 8) diff --git a/tests/translator/input/api_with_custom_domain_route53_multiple.yaml b/tests/translator/input/api_with_custom_domain_route53_multiple.yaml new file mode 100644 index 000000000..6bfe59c91 --- /dev/null +++ b/tests/translator/input/api_with_custom_domain_route53_multiple.yaml @@ -0,0 +1,67 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + apigateway-2402 + + Sample SAM Template for apigateway-2402 + +Resources: + + ApiGatewayAdminOne: + Type: AWS::Serverless::Api + Properties: + Name: App-Prod-Web + StageName: Prod + TracingEnabled: true + MethodSettings: + - LoggingLevel: Info + ResourcePath: '/*' + HttpMethod: '*' + Domain: + DomainName: admin.one.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" + EndpointConfiguration: + Type: REGIONAL + + + ApiGatewayAdminTwo: + Type: AWS::Serverless::Api + Properties: + Name: App-Prod-Web + StageName: Prod + TracingEnabled: true + MethodSettings: + - LoggingLevel: Info + ResourcePath: '/*' + HttpMethod: '*' + Domain: + DomainName: admin.two.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" + EndpointConfiguration: + Type: REGIONAL + + + ApiGatewayAdminThree: + Type: AWS::Serverless::Api + Properties: + Name: App-Prod-Web + StageName: Prod + TracingEnabled: true + MethodSettings: + - LoggingLevel: Info + ResourcePath: '/*' + HttpMethod: '*' + Domain: + DomainName: admin.three.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" + EndpointConfiguration: + Type: REGIONAL diff --git a/tests/translator/input/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.yaml b/tests/translator/input/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.yaml new file mode 100644 index 000000000..bb0eaf6b2 --- /dev/null +++ b/tests/translator/input/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.yaml @@ -0,0 +1,71 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + apigateway-2402 + + Sample SAM Template for apigateway-2402 + +Parameters: + MyHostedZoneId: + Type: String + +Resources: + + ApiGatewayAdminOne: + Type: AWS::Serverless::Api + Properties: + Name: App-Prod-Web + StageName: Prod + TracingEnabled: true + MethodSettings: + - LoggingLevel: Info + ResourcePath: '/*' + HttpMethod: '*' + Domain: + DomainName: admin.one.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: !Ref MyHostedZoneId + EndpointConfiguration: + Type: REGIONAL + + + ApiGatewayAdminTwo: + Type: AWS::Serverless::Api + Properties: + Name: App-Prod-Web + StageName: Prod + TracingEnabled: true + MethodSettings: + - LoggingLevel: Info + ResourcePath: '/*' + HttpMethod: '*' + Domain: + DomainName: admin.two.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: !Sub "{{MyHostedZoneId}}" + EndpointConfiguration: + Type: REGIONAL + + + ApiGatewayAdminThree: + Type: AWS::Serverless::Api + Properties: + Name: App-Prod-Web + StageName: Prod + TracingEnabled: true + MethodSettings: + - LoggingLevel: Info + ResourcePath: '/*' + HttpMethod: '*' + Domain: + DomainName: admin.three.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: !Ref MyHostedZoneId + EndpointConfiguration: + Type: REGIONAL diff --git a/tests/translator/input/api_with_fail_on_warnings.yaml b/tests/translator/input/api_with_fail_on_warnings.yaml new file mode 100644 index 000000000..354eeaf76 --- /dev/null +++ b/tests/translator/input/api_with_fail_on_warnings.yaml @@ -0,0 +1,22 @@ +Resources: + ApiGatewayApi: + Type: AWS::Serverless::Api + Properties: + StageName: prod + FailOnWarnings: true + ApiFunction: # Adds a GET api endpoint at "/" to the ApiGatewayApi via an Api event + Type: AWS::Serverless::Function + Properties: + Events: + ApiEvent: + Type: Api + Properties: + Path: / + Method: get + RestApiId: + Ref: ApiGatewayApi + Runtime: python3.7 + Handler: index.handler + InlineCode: | + def handler(event, context): + return {'body': 'Hello World!', 'statusCode': 200} diff --git a/tests/translator/input/error_api_invalid_fail_on_warnings.yaml b/tests/translator/input/error_api_invalid_fail_on_warnings.yaml new file mode 100644 index 000000000..c530ffd4c --- /dev/null +++ b/tests/translator/input/error_api_invalid_fail_on_warnings.yaml @@ -0,0 +1,22 @@ +Resources: + ApiGatewayApi: + Type: AWS::Serverless::Api + Properties: + StageName: prod + FailOnWarnings: '' + ApiFunction: # Adds a GET api endpoint at "/" to the ApiGatewayApi via an Api event + Type: AWS::Serverless::Function + Properties: + Events: + ApiEvent: + Type: Api + Properties: + Path: / + Method: get + RestApiId: + Ref: ApiGatewayApi + Runtime: python3.7 + Handler: index.handler + InlineCode: | + def handler(event, context): + return {'body': 'Hello World!', 'statusCode': 200} diff --git a/tests/translator/input/http_api_with_custom_domain_route53_multiple.yaml b/tests/translator/input/http_api_with_custom_domain_route53_multiple.yaml new file mode 100644 index 000000000..21832b61d --- /dev/null +++ b/tests/translator/input/http_api_with_custom_domain_route53_multiple.yaml @@ -0,0 +1,30 @@ +Resources: + MyApi1: + Type: AWS::Serverless::HttpApi + Properties: + Domain: + DomainName: admin.one.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" + + MyApi2: + Type: AWS::Serverless::HttpApi + Properties: + Domain: + DomainName: admin.two.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" + + MyApi3: + Type: AWS::Serverless::HttpApi + Properties: + Domain: + DomainName: admin.three.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" \ No newline at end of file diff --git a/tests/translator/input/mixed_api_with_custom_domain_route53_multiple.yaml b/tests/translator/input/mixed_api_with_custom_domain_route53_multiple.yaml new file mode 100644 index 000000000..7461e530c --- /dev/null +++ b/tests/translator/input/mixed_api_with_custom_domain_route53_multiple.yaml @@ -0,0 +1,36 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + apigateway-2402 + + Sample SAM Template for apigateway-2402 + +Resources: + MyHttpApi: + Type: AWS::Serverless::HttpApi + Properties: + Domain: + DomainName: admin.one.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" + + MyRestApi: + Type: AWS::Serverless::Api + Properties: + Name: App-Prod-Web + StageName: Prod + TracingEnabled: true + MethodSettings: + - LoggingLevel: Info + ResourcePath: '/*' + HttpMethod: '*' + Domain: + DomainName: admin.two.amazon.com + CertificateArn: arn::cert::abc + EndpointConfiguration: REGIONAL + Route53: + HostedZoneId: "abc123456" + EndpointConfiguration: + Type: REGIONAL diff --git a/tests/translator/output/api_with_custom_domain_route53_multiple.json b/tests/translator/output/api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..22f774448 --- /dev/null +++ b/tests/translator/output/api_with_custom_domain_route53_multiple.json @@ -0,0 +1,308 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Resources": { + "ApiGatewayAdminTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminTwoDeployment61887a4eed" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminOneBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName5fe29fe649" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "Stage": { + "Ref": "ApiGatewayAdminOneProdStage" + } + } + }, + "ApiGatewayAdminOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminOneDeploymentdd3f545183" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminThreeDeployment7541e97159": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 7541e971598cffe7cafab030d3fccc687d508f59", + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminOneDeploymentdd3f545183": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: dd3f545183668c401e771fd9a377cfeadcf88a35", + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Stage" + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminTwoDeployment61887a4eed": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 61887a4eed03102402cbaa575b5b1e398b0dc647", + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName5fe29fe649": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.one.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminTwoBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "Stage": { + "Ref": "ApiGatewayAdminTwoProdStage" + } + } + }, + "ApiGatewayAdminTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName41bfc7f9c4": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.three.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminThreeDeployment7541e97159" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminThreeBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName41bfc7f9c4" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "Stage": { + "Ref": "ApiGatewayAdminThreeProdStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json b/tests/translator/output/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json new file mode 100644 index 000000000..26b978e90 --- /dev/null +++ b/tests/translator/output/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json @@ -0,0 +1,325 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Parameters": { + "MyHostedZoneId": { + "Type": "String" + } + }, + "Resources": { + "RecordSetGroupd9cb5a3e02": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": { + "Fn::Sub": "{{MyHostedZoneId}}" + }, + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminTwoDeploymentca2a75b5dd" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayDomainName5fe29fe649": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.one.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminOneDeployment066bb1ceae" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName41bfc7f9c4" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "Stage": { + "Ref": "ApiGatewayAdminThreeProdStage" + } + } + }, + "ApiGatewayAdminOneBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName5fe29fe649" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "Stage": { + "Ref": "ApiGatewayAdminOneProdStage" + } + } + }, + "RecordSetGroupd28e0e19d0": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": { + "Ref": "MyHostedZoneId" + }, + "RecordSets": [ + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminOneDeployment066bb1ceae": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 066bb1ceaebd0cafae99258bbe7130af8b676372", + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminTwoBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "Stage": { + "Ref": "ApiGatewayAdminTwoProdStage" + } + } + }, + "ApiGatewayAdminTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName41bfc7f9c4": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.three.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminThreeDeployment169349c1e9" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminTwoDeploymentca2a75b5dd": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: ca2a75b5dd3713c71543e80f2b6f5aac9538ea9c", + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeDeployment169349c1e9": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 169349c1e96a0f130ee35f7bb9d83b042c386d6f", + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_fail_on_warnings.json b/tests/translator/output/api_with_fail_on_warnings.json new file mode 100644 index 000000000..6764a4080 --- /dev/null +++ b/tests/translator/output/api_with_fail_on_warnings.json @@ -0,0 +1,128 @@ +{ + "Resources": { + "ApiGatewayApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "FailOnWarnings": true + } + }, + "ApiGatewayApiDeploymentf96bc9abda": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ApiGatewayApi" + }, + "Description": "RestApi deployment id: f96bc9abdad53c001153ce8ba04f1667c7b0a004", + "StageName": "Stage" + } + }, + "ApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "def handler(event, context):\n return {'body': 'Hello World!', 'statusCode': 200}\n" + }, + "Role": { + "Fn::GetAtt": [ + "ApiFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiFunctionApiEventPermissionprod": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ApiGatewayApi" + } + } + ] + } + } + }, + "ApiGatewayApiprodStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayApiDeploymentf96bc9abda" + }, + "RestApiId": { + "Ref": "ApiGatewayApi" + }, + "StageName": "prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_custom_domain_route53_multiple.json b/tests/translator/output/aws-cn/api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..22f774448 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_custom_domain_route53_multiple.json @@ -0,0 +1,308 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Resources": { + "ApiGatewayAdminTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminTwoDeployment61887a4eed" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminOneBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName5fe29fe649" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "Stage": { + "Ref": "ApiGatewayAdminOneProdStage" + } + } + }, + "ApiGatewayAdminOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminOneDeploymentdd3f545183" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminThreeDeployment7541e97159": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 7541e971598cffe7cafab030d3fccc687d508f59", + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminOneDeploymentdd3f545183": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: dd3f545183668c401e771fd9a377cfeadcf88a35", + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Stage" + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminTwoDeployment61887a4eed": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 61887a4eed03102402cbaa575b5b1e398b0dc647", + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName5fe29fe649": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.one.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminTwoBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "Stage": { + "Ref": "ApiGatewayAdminTwoProdStage" + } + } + }, + "ApiGatewayAdminTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName41bfc7f9c4": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.three.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminThreeDeployment7541e97159" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminThreeBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName41bfc7f9c4" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "Stage": { + "Ref": "ApiGatewayAdminThreeProdStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json b/tests/translator/output/aws-cn/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json new file mode 100644 index 000000000..26b978e90 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json @@ -0,0 +1,325 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Parameters": { + "MyHostedZoneId": { + "Type": "String" + } + }, + "Resources": { + "RecordSetGroupd9cb5a3e02": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": { + "Fn::Sub": "{{MyHostedZoneId}}" + }, + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminTwoDeploymentca2a75b5dd" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayDomainName5fe29fe649": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.one.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminOneDeployment066bb1ceae" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName41bfc7f9c4" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "Stage": { + "Ref": "ApiGatewayAdminThreeProdStage" + } + } + }, + "ApiGatewayAdminOneBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName5fe29fe649" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "Stage": { + "Ref": "ApiGatewayAdminOneProdStage" + } + } + }, + "RecordSetGroupd28e0e19d0": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": { + "Ref": "MyHostedZoneId" + }, + "RecordSets": [ + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminOneDeployment066bb1ceae": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 066bb1ceaebd0cafae99258bbe7130af8b676372", + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminTwoBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "Stage": { + "Ref": "ApiGatewayAdminTwoProdStage" + } + } + }, + "ApiGatewayAdminTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName41bfc7f9c4": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.three.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminThreeDeployment169349c1e9" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminTwoDeploymentca2a75b5dd": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: ca2a75b5dd3713c71543e80f2b6f5aac9538ea9c", + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeDeployment169349c1e9": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 169349c1e96a0f130ee35f7bb9d83b042c386d6f", + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_fail_on_warnings.json b/tests/translator/output/aws-cn/api_with_fail_on_warnings.json new file mode 100644 index 000000000..97c968816 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_fail_on_warnings.json @@ -0,0 +1,136 @@ +{ + "Resources": { + "ApiGatewayApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "FailOnWarnings": true, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ApiGatewayApiDeploymentb0ed1521b2": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ApiGatewayApi" + }, + "Description": "RestApi deployment id: b0ed1521b2c5e65da74d55d16b139143ae483503", + "StageName": "Stage" + } + }, + "ApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "def handler(event, context):\n return {'body': 'Hello World!', 'statusCode': 200}\n" + }, + "Role": { + "Fn::GetAtt": [ + "ApiFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiFunctionApiEventPermissionprod": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ApiGatewayApi" + } + } + ] + } + } + }, + "ApiGatewayApiprodStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayApiDeploymentb0ed1521b2" + }, + "RestApiId": { + "Ref": "ApiGatewayApi" + }, + "StageName": "prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/http_api_with_custom_domain_route53_multiple.json b/tests/translator/output/aws-cn/http_api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..1be40930c --- /dev/null +++ b/tests/translator/output/aws-cn/http_api_with_custom_domain_route53_multiple.json @@ -0,0 +1,255 @@ +{ + "Resources": { + "MyApi1": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV25fe29fe649": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.one.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi1ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi1" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV25fe29fe649" + }, + "Stage": { + "Ref": "MyApi1ApiGatewayDefaultStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV23fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV23fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV241bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV241bfc7f9c4", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "MyApi1ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi1" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyApi2": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV23fd2dbd8f8": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.two.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi2ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi2" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV23fd2dbd8f8" + }, + "Stage": { + "Ref": "MyApi2ApiGatewayDefaultStage" + } + } + }, + "MyApi2ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi2" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyApi3": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV241bfc7f9c4": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.three.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi3ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi3" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV241bfc7f9c4" + }, + "Stage": { + "Ref": "MyApi3ApiGatewayDefaultStage" + } + } + }, + "MyApi3ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi3" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/mixed_api_with_custom_domain_route53_multiple.json b/tests/translator/output/aws-cn/mixed_api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..025997925 --- /dev/null +++ b/tests/translator/output/aws-cn/mixed_api_with_custom_domain_route53_multiple.json @@ -0,0 +1,193 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Resources": { + "MyRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyHttpApiApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV25fe29fe649" + }, + "Stage": { + "Ref": "MyHttpApiApiGatewayDefaultStage" + } + } + }, + "MyRestApiDeployment61887a4eed": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 61887a4eed03102402cbaa575b5b1e398b0dc647", + "RestApiId": { + "Ref": "MyRestApi" + }, + "StageName": "Stage" + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainNameV25fe29fe649": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.one.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyHttpApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "MyHttpApiApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyRestApiDeployment61887a4eed" + }, + "RestApiId": { + "Ref": "MyRestApi" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "MyRestApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "MyRestApi" + }, + "Stage": { + "Ref": "MyRestApiProdStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_multiple.json b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..22f774448 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_multiple.json @@ -0,0 +1,308 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Resources": { + "ApiGatewayAdminTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminTwoDeployment61887a4eed" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminOneBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName5fe29fe649" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "Stage": { + "Ref": "ApiGatewayAdminOneProdStage" + } + } + }, + "ApiGatewayAdminOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminOneDeploymentdd3f545183" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminThreeDeployment7541e97159": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 7541e971598cffe7cafab030d3fccc687d508f59", + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminOneDeploymentdd3f545183": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: dd3f545183668c401e771fd9a377cfeadcf88a35", + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Stage" + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminTwoDeployment61887a4eed": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 61887a4eed03102402cbaa575b5b1e398b0dc647", + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName5fe29fe649": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.one.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminTwoBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "Stage": { + "Ref": "ApiGatewayAdminTwoProdStage" + } + } + }, + "ApiGatewayAdminTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName41bfc7f9c4": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.three.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminThreeDeployment7541e97159" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminThreeBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName41bfc7f9c4" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "Stage": { + "Ref": "ApiGatewayAdminThreeProdStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json new file mode 100644 index 000000000..26b978e90 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid.json @@ -0,0 +1,325 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Parameters": { + "MyHostedZoneId": { + "Type": "String" + } + }, + "Resources": { + "RecordSetGroupd9cb5a3e02": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": { + "Fn::Sub": "{{MyHostedZoneId}}" + }, + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminTwoProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminTwoDeploymentca2a75b5dd" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayDomainName5fe29fe649": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.one.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminOneProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminOneDeployment066bb1ceae" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName41bfc7f9c4" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "Stage": { + "Ref": "ApiGatewayAdminThreeProdStage" + } + } + }, + "ApiGatewayAdminOneBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName5fe29fe649" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "Stage": { + "Ref": "ApiGatewayAdminOneProdStage" + } + } + }, + "RecordSetGroupd28e0e19d0": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": { + "Ref": "MyHostedZoneId" + }, + "RecordSets": [ + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName41bfc7f9c4", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName5fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "ApiGatewayAdminOneDeployment066bb1ceae": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 066bb1ceaebd0cafae99258bbe7130af8b676372", + "RestApiId": { + "Ref": "ApiGatewayAdminOne" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminTwoBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "Stage": { + "Ref": "ApiGatewayAdminTwoProdStage" + } + } + }, + "ApiGatewayAdminTwo": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainName41bfc7f9c4": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.three.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayAdminThreeDeployment169349c1e9" + }, + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "ApiGatewayAdminTwoDeploymentca2a75b5dd": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: ca2a75b5dd3713c71543e80f2b6f5aac9538ea9c", + "RestApiId": { + "Ref": "ApiGatewayAdminTwo" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminThree": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayAdminThreeDeployment169349c1e9": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 169349c1e96a0f130ee35f7bb9d83b042c386d6f", + "RestApiId": { + "Ref": "ApiGatewayAdminThree" + }, + "StageName": "Stage" + } + }, + "ApiGatewayAdminOne": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_fail_on_warnings.json b/tests/translator/output/aws-us-gov/api_with_fail_on_warnings.json new file mode 100644 index 000000000..2aece6d1b --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_fail_on_warnings.json @@ -0,0 +1,136 @@ +{ + "Resources": { + "ApiGatewayApiDeployment4539d6d48c": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ApiGatewayApi" + }, + "Description": "RestApi deployment id: 4539d6d48c1b3fdc71e492190aa2eeff27526d7f", + "StageName": "Stage" + } + }, + "ApiGatewayApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0" + }, + "FailOnWarnings": true, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "ZipFile": "def handler(event, context):\n return {'body': 'Hello World!', 'statusCode': 200}\n" + }, + "Role": { + "Fn::GetAtt": [ + "ApiFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ApiFunctionApiEventPermissionprod": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ApiGatewayApi" + } + } + ] + } + } + }, + "ApiGatewayApiprodStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "ApiGatewayApiDeployment4539d6d48c" + }, + "RestApiId": { + "Ref": "ApiGatewayApi" + }, + "StageName": "prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/http_api_with_custom_domain_route53_multiple.json b/tests/translator/output/aws-us-gov/http_api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..1be40930c --- /dev/null +++ b/tests/translator/output/aws-us-gov/http_api_with_custom_domain_route53_multiple.json @@ -0,0 +1,255 @@ +{ + "Resources": { + "MyApi1": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV25fe29fe649": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.one.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi1ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi1" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV25fe29fe649" + }, + "Stage": { + "Ref": "MyApi1ApiGatewayDefaultStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV23fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV23fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV241bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV241bfc7f9c4", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "MyApi1ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi1" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyApi2": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV23fd2dbd8f8": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.two.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi2ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi2" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV23fd2dbd8f8" + }, + "Stage": { + "Ref": "MyApi2ApiGatewayDefaultStage" + } + } + }, + "MyApi2ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi2" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyApi3": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV241bfc7f9c4": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.three.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi3ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi3" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV241bfc7f9c4" + }, + "Stage": { + "Ref": "MyApi3ApiGatewayDefaultStage" + } + } + }, + "MyApi3ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi3" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/mixed_api_with_custom_domain_route53_multiple.json b/tests/translator/output/aws-us-gov/mixed_api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..025997925 --- /dev/null +++ b/tests/translator/output/aws-us-gov/mixed_api_with_custom_domain_route53_multiple.json @@ -0,0 +1,193 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Resources": { + "MyRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyHttpApiApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV25fe29fe649" + }, + "Stage": { + "Ref": "MyHttpApiApiGatewayDefaultStage" + } + } + }, + "MyRestApiDeployment61887a4eed": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 61887a4eed03102402cbaa575b5b1e398b0dc647", + "RestApiId": { + "Ref": "MyRestApi" + }, + "StageName": "Stage" + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainNameV25fe29fe649": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.one.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyHttpApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "MyHttpApiApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyRestApiDeployment61887a4eed" + }, + "RestApiId": { + "Ref": "MyRestApi" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "MyRestApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "MyRestApi" + }, + "Stage": { + "Ref": "MyRestApiProdStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/error_api_invalid_fail_on_warnings.json b/tests/translator/output/error_api_invalid_fail_on_warnings.json new file mode 100644 index 000000000..4f7a202c7 --- /dev/null +++ b/tests/translator/output/error_api_invalid_fail_on_warnings.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [ApiGatewayApi] is invalid. FailOnWarnings cannot be empty." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [ApiGatewayApi] is invalid. Type of property 'FailOnWarnings' is invalid." + } \ No newline at end of file diff --git a/tests/translator/output/http_api_with_custom_domain_route53_multiple.json b/tests/translator/output/http_api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..1be40930c --- /dev/null +++ b/tests/translator/output/http_api_with_custom_domain_route53_multiple.json @@ -0,0 +1,255 @@ +{ + "Resources": { + "MyApi1": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV25fe29fe649": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.one.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi1ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi1" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV25fe29fe649" + }, + "Stage": { + "Ref": "MyApi1ApiGatewayDefaultStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV23fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV23fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.three.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV241bfc7f9c4", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV241bfc7f9c4", + "RegionalDomainName" + ] + } + } + } + ] + } + }, + "MyApi1ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi1" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyApi2": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV23fd2dbd8f8": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.two.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi2ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi2" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV23fd2dbd8f8" + }, + "Stage": { + "Ref": "MyApi2ApiGatewayDefaultStage" + } + } + }, + "MyApi2ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi2" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyApi3": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "ApiGatewayDomainNameV241bfc7f9c4": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.three.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyApi3ApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyApi3" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV241bfc7f9c4" + }, + "Stage": { + "Ref": "MyApi3ApiGatewayDefaultStage" + } + } + }, + "MyApi3ApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyApi3" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/mixed_api_with_custom_domain_route53_multiple.json b/tests/translator/output/mixed_api_with_custom_domain_route53_multiple.json new file mode 100644 index 000000000..025997925 --- /dev/null +++ b/tests/translator/output/mixed_api_with_custom_domain_route53_multiple.json @@ -0,0 +1,193 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "apigateway-2402\nSample SAM Template for apigateway-2402\n", + "Resources": { + "MyRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "swagger": "2.0" + }, + "Name": "App-Prod-Web", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "MyHttpApiApiMapping": { + "Type": "AWS::ApiGatewayV2::ApiMapping", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi" + }, + "DomainName": { + "Ref": "ApiGatewayDomainNameV25fe29fe649" + }, + "Stage": { + "Ref": "MyHttpApiApiGatewayDefaultStage" + } + } + }, + "MyRestApiDeployment61887a4eed": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 61887a4eed03102402cbaa575b5b1e398b0dc647", + "RestApiId": { + "Ref": "MyRestApi" + }, + "StageName": "Stage" + } + }, + "ApiGatewayDomainName3fd2dbd8f8": { + "Type": "AWS::ApiGateway::DomainName", + "Properties": { + "RegionalCertificateArn": "arn::cert::abc", + "DomainName": "admin.two.amazon.com", + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + } + } + }, + "ApiGatewayDomainNameV25fe29fe649": { + "Type": "AWS::ApiGatewayV2::DomainName", + "Properties": { + "DomainName": "admin.one.amazon.com", + "DomainNameConfigurations": [ + { + "EndpointType": "REGIONAL", + "CertificateArn": "arn::cert::abc" + } + ], + "Tags": { + "httpapi:createdBy": "SAM" + } + } + }, + "MyHttpApi": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": {}, + "openapi": "3.0.1", + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + } + }, + "MyHttpApiApiGatewayDefaultStage": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi" + }, + "StageName": "$default", + "Tags": { + "httpapi:createdBy": "SAM" + }, + "AutoDeploy": true + } + }, + "MyRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyRestApiDeployment61887a4eed" + }, + "RestApiId": { + "Ref": "MyRestApi" + }, + "StageName": "Prod", + "TracingEnabled": true, + "MethodSettings": [ + { + "HttpMethod": "*", + "ResourcePath": "/*", + "LoggingLevel": "Info" + } + ] + } + }, + "MyRestApiBasePathMapping": { + "Type": "AWS::ApiGateway::BasePathMapping", + "Properties": { + "DomainName": { + "Ref": "ApiGatewayDomainName3fd2dbd8f8" + }, + "RestApiId": { + "Ref": "MyRestApi" + }, + "Stage": { + "Ref": "MyRestApiProdStage" + } + } + }, + "RecordSetGroup370194ff6e": { + "Type": "AWS::Route53::RecordSetGroup", + "Properties": { + "HostedZoneId": "abc123456", + "RecordSets": [ + { + "Name": "admin.two.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainName3fd2dbd8f8", + "RegionalDomainName" + ] + } + } + }, + { + "Name": "admin.one.amazon.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalHostedZoneId" + ] + }, + "DNSName": { + "Fn::GetAtt": [ + "ApiGatewayDomainNameV25fe29fe649", + "RegionalDomainName" + ] + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index b7230f4dd..a0b102879 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -299,6 +299,8 @@ def test_transform_success(self, testcase, partition_with_region): "api_with_basic_custom_domain_intrinsics", "api_with_custom_domain_route53", "api_with_custom_domain_route53_hosted_zone_name", + "api_with_custom_domain_route53_multiple", + "api_with_custom_domain_route53_multiple_intrinsic_hostedzoneid", "api_with_basic_custom_domain_http", "api_with_basic_custom_domain_intrinsics_http", "api_with_custom_domain_route53_http", @@ -322,6 +324,8 @@ def test_transform_success(self, testcase, partition_with_region): "http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer", "http_api_local_iam_auth_enabled", "http_api_multiple_authorizers", + "http_api_with_custom_domain_route53_multiple", + "mixed_api_with_custom_domain_route53_multiple", ], [ ("aws", "ap-southeast-1"),