diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 933361a0d..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,28 +0,0 @@ - - -**Description:** - - - -**Steps to reproduce the issue:** -1. -2. -3. - -**Observed result:** - -**Expected result:** - diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 27d29d044..bd9c12d27 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report about: Create an issue to report a bug for the SAM Translator -title: '' -labels: '' +title: "Bug: TITLE" +labels: ['type/bug', 'stage/needs-triage'] assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..ec4bb386b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 144feb3fb..64240dabc 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,8 +1,8 @@ --- name: Feature request about: Suggest an idea/feature/enhancement for the SAM Translator -title: '' -labels: '' +title: "Feature request: TITLE" +labels: ['type/feature', 'stage/needs-triage'] assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md new file mode 100644 index 000000000..a2863345c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.md @@ -0,0 +1,8 @@ +--- +name: Other +about: Choose if your issue doesn't apply to the other templates +title: '' +labels: ['stage/needs-triage'] +assignees: '' + +--- \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 150fc8f02..e398ec854 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,6 +14,9 @@ - [ ] `make pr` passes - [ ] Update documentation - [ ] Verify transformed template deploys and application functions as expected +- [ ] Do these changes include any template validations? + - [ ] Did the newly validated properties support intrinsics prior to adding the validations? (If unsure, please review [Intrinsic Functions](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html) before proceeding). + - [ ] Does the pull request ensure that intrinsics remain functional with the new validations? *Examples?* diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index e4d50db9d..434e6ada4 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -26,6 +26,6 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - labels: ['pr/external'] + labels: ['pr/external', 'stage/needs-triage'] }) } diff --git a/DEVELOPMENT_GUIDE.md b/DEVELOPMENT_GUIDE.md index 76486e538..d260c6c70 100644 --- a/DEVELOPMENT_GUIDE.md +++ b/DEVELOPMENT_GUIDE.md @@ -114,7 +114,9 @@ Running Tests ### Unit testing with one Python version -If you're trying to do a quick run, it's ok to use the current python version. Run `make pr`. +If you're trying to do a quick run, it's ok to use the current python version. +Run `make test` or `make test-fast`. Once all tests pass make sure to run +`make pr` before sending out your PR. ### Unit testing with multiple Python versions diff --git a/Makefile b/Makefile index 14d2ca642..4692a5e59 100755 --- a/Makefile +++ b/Makefile @@ -1,13 +1,16 @@ target: $(info ${HELP_MESSAGE}) @exit 0 - + init: pip install -e '.[dev]' test: pytest --cov samtranslator --cov-report term-missing --cov-fail-under 95 -n auto tests/* +test-fast: + pytest -x --cov samtranslator --cov-report term-missing --cov-fail-under 95 -n auto tests/* + test-cov-report: pytest --cov samtranslator --cov-report term-missing --cov-report html --cov-fail-under 95 tests/* diff --git a/bin/sam-translate.py b/bin/sam-translate.py index e533e2949..30874795c 100755 --- a/bin/sam-translate.py +++ b/bin/sam-translate.py @@ -37,7 +37,6 @@ from samtranslator.translator.transform import transform from samtranslator.yaml_helper import yaml_parse from samtranslator.model.exceptions import InvalidDocumentException -from samtranslator.feature_toggle.feature_toggle import FeatureToggleLocalConfigProvider, FeatureToggle LOG = logging.getLogger(__name__) cli_options = docopt(__doc__) @@ -98,16 +97,8 @@ def transform_template(input_file_path, output_file_path): sam_template = yaml_parse(f) try: - feature_toggle = FeatureToggle( - FeatureToggleLocalConfigProvider( - os.path.join(my_path, "..", "tests", "feature_toggle", "input", "feature_toggle_config.json") - ), - stage=None, - account_id=None, - region=None, - ) - cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client), feature_toggle) - cloud_formation_template_prettified = json.dumps(cloud_formation_template, indent=2) + cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client)) + cloud_formation_template_prettified = json.dumps(cloud_formation_template, indent=1) with open(output_file_path, "w") as f: f.write(cloud_formation_template_prettified) diff --git a/docs/policy_templates.rst b/docs/policy_templates.rst index 1cd229243..2e6a2da87 100644 --- a/docs/policy_templates.rst +++ b/docs/policy_templates.rst @@ -15,7 +15,7 @@ For Example: Properties: ... Policies: - # Give DynamoDB Full Access to your Lambda Function + # Give your Lambda Function Full Access to DynamoDB - AmazonDynamoDBFullAccess ... diff --git a/integration/combination/test_api_with_authorizer_apikey.py b/integration/combination/test_api_with_authorizer_apikey.py new file mode 100644 index 000000000..c0a11cbbc --- /dev/null +++ b/integration/combination/test_api_with_authorizer_apikey.py @@ -0,0 +1,130 @@ +from unittest.case import skipIf + +import requests + +from integration.helpers.base_test import BaseTest +from integration.helpers.deployer.utils.retry import retry +from integration.helpers.exception import StatusCodeError +from integration.helpers.resource import current_region_does_not_support +from integration.config.service_names import COGNITO + + +class TestApiWithAuthorizerApiKey(BaseTest): + def test_authorizer_apikey(self): + self.create_and_verify_stack("combination/api_with_authorizer_apikey") + stack_outputs = self.get_stack_outputs() + + rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi") + apigw_client = self.client_provider.api_client + + authorizers = apigw_client.get_authorizers(restApiId=rest_api_id)["items"] + lambda_authorizer_uri = ( + "arn:aws:apigateway:" + + self.my_region + + ":lambda:path/2015-03-31/functions/" + + stack_outputs["AuthorizerFunctionArn"] + + "/invocations" + ) + + lambda_token_authorizer = get_authorizer_by_name(authorizers, "MyLambdaTokenAuth") + self.assertEqual(lambda_token_authorizer["type"], "TOKEN", "lambdaTokenAuthorizer: Type must be TOKEN") + self.assertEqual( + lambda_token_authorizer["identitySource"], + "method.request.header.Authorization", + "lambdaTokenAuthorizer: identity source must be method.request.header.Authorization", + ) + self.assertIsNone( + lambda_token_authorizer.get("authorizerCredentials"), + "lambdaTokenAuthorizer: authorizer credentials must not be set", + ) + self.assertIsNone( + lambda_token_authorizer.get("identityValidationExpression"), + "lambdaTokenAuthorizer: validation expression must not be set", + ) + self.assertEqual( + lambda_token_authorizer["authorizerUri"], + lambda_authorizer_uri, + "lambdaTokenAuthorizer: authorizer URI must be the Lambda Function Authorizer's URI", + ) + self.assertIsNone( + lambda_token_authorizer.get("authorizerResultTtlInSeconds"), "lambdaTokenAuthorizer: TTL must not be set" + ) + + resources = apigw_client.get_resources(restApiId=rest_api_id)["items"] + + lambda_token_get_method_result = get_method(resources, "/lambda-token-api-key", rest_api_id, apigw_client) + self.assertEqual( + lambda_token_get_method_result["authorizerId"], + lambda_token_authorizer["id"], + "lambdaTokenAuthorizer: GET method must be configured to use the Lambda Token Authorizer", + ) + + base_url = stack_outputs["ApiUrl"] + + self.verify_authorized_request(base_url + "none", 200) + self.verify_authorized_request(base_url + "lambda-token-api-key", 401) + # ApiKeySourceType is AUTHORIZER. This will trigger the Lambda Authorizer and in turn returns the api key + self.verify_authorized_request(base_url + "lambda-token-api-key", 200, "Authorization", "allow") + + api_key_id = stack_outputs["ApiKeyId"] + key = apigw_client.get_api_key(apiKey=api_key_id, includeValue=True) + + self.verify_authorized_request(base_url + "lambda-token-api-key", 401) + # ApiKeySourceType is AUTHORIZER. Passing api key via x-api-key will not get authorized + self.verify_authorized_request(base_url + "lambda-token-api-key", 401, "x-api-key", key["value"]) + + @retry(StatusCodeError, 10) + def verify_authorized_request( + self, + url, + expected_status_code, + header_key=None, + header_value=None, + ): + if not header_key or not header_value: + response = requests.get(url) + else: + headers = {header_key: header_value} + response = requests.get(url, headers=headers) + status = response.status_code + if status != expected_status_code: + raise StatusCodeError( + "Request to {} failed with status: {}, expected status: {}".format(url, status, expected_status_code) + ) + + if not header_key or not header_value: + self.assertEqual( + status, expected_status_code, "Request to " + url + " must return HTTP " + str(expected_status_code) + ) + else: + self.assertEqual( + status, + expected_status_code, + "Request to " + + url + + " (" + + header_key + + ": " + + header_value + + ") must return HTTP " + + str(expected_status_code), + ) + + +def get_authorizer_by_name(authorizers, name): + for authorizer in authorizers: + if authorizer["name"] == name: + return authorizer + return None + + +def get_resource_by_path(resources, path): + for resource in resources: + if resource["path"] == path: + return resource + return None + + +def get_method(resources, path, rest_api_id, apigw_client): + resource = get_resource_by_path(resources, path) + return apigw_client.get_method(restApiId=rest_api_id, resourceId=resource["id"], httpMethod="GET") diff --git a/integration/combination/test_custom_http_api_domains_test.py b/integration/combination/test_custom_http_api_domains_test.py new file mode 100644 index 000000000..cde5e2435 --- /dev/null +++ b/integration/combination/test_custom_http_api_domains_test.py @@ -0,0 +1,48 @@ +from unittest.case import skipIf + +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 + + +@skipIf( + current_region_not_included([CUSTOM_DOMAIN]), + "CustomDomain is not supported in this testing region", +) +class TestCustomHttpApiDomains(BaseInternalTest): + def test_custom_http_api_domains_regional(self): + self.create_and_verify_stack("combination/http_api_with_custom_domains_regional") + + domain_name_list = self.get_stack_resources("AWS::ApiGatewayV2::DomainName") + self.assertEqual(1, len(domain_name_list)) + + domain_name_id = self.get_physical_id_by_type("AWS::ApiGatewayV2::DomainName") + + api_gateway_client = self.client_provider.api_v2_client + result = api_gateway_client.get_domain_name(DomainName=domain_name_id) + + 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"]) + + domain_name_configs = result["DomainNameConfigurations"] + self.assertEqual(1, len(domain_name_configs)) + domain_name_config = domain_name_configs[0] + + self.assertEqual("REGIONAL", domain_name_config["EndpointType"]) + self.assertEqual("TLS_1_2", domain_name_config["SecurityPolicy"]) + + def test_custom_http_api_domains_regional_ownership_verification(self): + self.create_and_verify_stack("combination/http_api_with_custom_domains_regional_ownership_verification") + + domain_name_id = self.get_physical_id_by_type("AWS::ApiGatewayV2::DomainName") + api_gateway_client = self.client_provider.api_v2_client + result = api_gateway_client.get_domain_name(DomainName=domain_name_id) + + domain_name_configs = result["DomainNameConfigurations"] + self.assertEqual(1, len(domain_name_configs)) + domain_name_config = domain_name_configs[0] + + self.assertIsNotNone(domain_name_config.get("OwnershipVerificationCertificateArn")) diff --git a/integration/combination/test_custom_rest_api_domains.py b/integration/combination/test_custom_rest_api_domains.py new file mode 100644 index 000000000..79618aca8 --- /dev/null +++ b/integration/combination/test_custom_rest_api_domains.py @@ -0,0 +1,59 @@ +from unittest.case import skipIf + +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 + + +@skipIf( + current_region_not_included([CUSTOM_DOMAIN]), + "CustomDomain is not supported in this testing region", +) +class TestCustomRestApiDomains(BaseInternalTest): + def test_custom_rest_api_domains_edge(self): + self.create_and_verify_stack("combination/api_with_custom_domains_edge") + domain_name_list = self.get_stack_resources("AWS::ApiGateway::DomainName") + self.assertEqual(1, len(domain_name_list)) + + domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName") + api_gateway_client = self.client_provider.api_client + result = api_gateway_client.get_domain_name(domainName=domain_name_id) + + self.assertEqual("sam-gamma-edge.com", result["domainName"]) + + end_point_config = result["endpointConfiguration"] + end_point_types = end_point_config["types"] + self.assertEqual(1, len(end_point_types)) + self.assertEqual("EDGE", end_point_types[0]) + + def test_custom_rest_api_domains_regional(self): + self.create_and_verify_stack("combination/api_with_custom_domains_regional") + + domain_name_list = self.get_stack_resources("AWS::ApiGateway::DomainName") + self.assertEqual(1, len(domain_name_list)) + + domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName") + + api_gateway_client = self.client_provider.api_client + result = api_gateway_client.get_domain_name(domainName=domain_name_id) + + self.assertEqual("sam-gamma-regional.com", result["domainName"]) + self.assertEqual("TLS_1_2", result["securityPolicy"]) + + end_point_config = result["endpointConfiguration"] + end_point_types = end_point_config["types"] + self.assertEqual(1, len(end_point_types)) + 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"]) + + def test_custom_rest_api_domains_regional_ownership_verification(self): + self.create_and_verify_stack("combination/api_with_custom_domains_regional_ownership_verification") + + domain_name_id = self.get_physical_id_by_type("AWS::ApiGateway::DomainName") + api_gateway_client = self.client_provider.api_client + result = api_gateway_client.get_domain_name(domainName=domain_name_id) + + self.assertIsNotNone(result.get("ownershipVerificationCertificateArn")) diff --git a/integration/combination/test_function_with_alias.py b/integration/combination/test_function_with_alias.py index 5eb8d01aa..4018eadcc 100644 --- a/integration/combination/test_function_with_alias.py +++ b/integration/combination/test_function_with_alias.py @@ -18,8 +18,7 @@ def test_updating_version_by_changing_property_value(self): # Changing CodeUri should create a new version, and leave the existing version in tact self.set_template_resource_property("MyLambdaFunction", "CodeUri", self.file_to_s3_uri_map["code2.zip"]["uri"]) - self.transform_template() - self.deploy_stack() + self.update_stack() version_ids = self.get_function_version_by_name(function_name) self.assertEqual(["1", "2"], version_ids) @@ -43,8 +42,7 @@ def test_alias_deletion_must_retain_version(self): # Check that the DeletionPolicy on Lambda Version holds good # Remove alias, update stack, and verify the version still exists by calling Lambda APIs self.remove_template_resource_property("MyLambdaFunction", "AutoPublishAlias") - self.transform_template() - self.deploy_stack() + self.update_stack() # Make sure both Lambda version & alias resource does not exist in stack alias = self.get_stack_resources("AWS::Lambda::Alias") @@ -71,13 +69,13 @@ def test_function_with_alias_with_intrinsics(self): # Let's change Key by updating the template parameter, but keep template same # This should create a new version and leave existing version intact parameters[1]["ParameterValue"] = "code2.zip" - # self.deploy_stack(parameters) - self.update_stack("combination/function_with_alias_intrinsics", parameters) + + self.update_stack(parameters) version_ids = get_function_versions(function_name, self.client_provider.lambda_client) + self.assertEqual(["1", "2"], version_ids) - self.assertEqual(["1"], version_ids) alias = self.get_alias(function_name, alias_name) - self.assertEqual("1", alias["FunctionVersion"]) + self.assertEqual("2", alias["FunctionVersion"]) def test_alias_in_globals_with_overrides(self): # It is good enough if we can create a stack. Globals are pre-processed on the SAM template and don't @@ -111,8 +109,7 @@ def test_alias_with_event_sources_get_correct_permissions(self): # Remove the alias, deploy the stack, and verify that *all* permission entities transfer to the function self.remove_template_resource_property("MyAwesomeFunction", "AutoPublishAlias") - self.transform_template() - self.deploy_stack() + self.update_stack() # Get the policies on both function & alias # Alias should have *no* policies diff --git a/integration/combination/test_function_with_deployment_preference.py b/integration/combination/test_function_with_deployment_preference.py index 7783ad317..d69e44d56 100644 --- a/integration/combination/test_function_with_deployment_preference.py +++ b/integration/combination/test_function_with_deployment_preference.py @@ -41,8 +41,7 @@ def test_flip_from_disable_to_enable(self): pref["Enabled"] = "True" self.set_template_resource_property("MyLambdaFunction", "DeploymentPreference", pref) - self.transform_template() - self.deploy_stack(self.get_default_test_template_parameters()) + self.update_stack(self.get_default_test_template_parameters()) self._verify_no_deployment_then_update_and_verify_deployment(self.get_default_test_template_parameters()) @@ -77,8 +76,7 @@ def _verify_no_deployment_then_update_and_verify_deployment(self, parameters=Non self.set_template_resource_property( LAMBDA_FUNCTION_NAME, "CodeUri", self.file_to_s3_uri_map["code2.zip"]["uri"] ) - self.transform_template() - self.deploy_stack(parameters) + self.update_stack(parameters) for deployment_group in deployment_groups: deployments = self._get_deployments(application_name, deployment_group) diff --git a/integration/combination/test_function_with_policy_templates.py b/integration/combination/test_function_with_policy_templates.py new file mode 100644 index 000000000..a73c384d5 --- /dev/null +++ b/integration/combination/test_function_with_policy_templates.py @@ -0,0 +1,70 @@ +from integration.helpers.base_test import BaseTest +from integration.helpers.common_api import get_policy_statements + + +class TestFunctionWithPolicyTemplates(BaseTest): + def test_with_policy_templates(self): + self.create_and_verify_stack("combination/function_with_policy_templates") + role_name = self.get_physical_id_by_type("AWS::IAM::Role") + + # There should be three policies created. Each policy has the name Policy + + # Verify the contents of first policy + sqs_poller_policy = get_policy_statements(role_name, "MyFunctionRolePolicy0", self.client_provider.iam_client) + self.assertEqual(len(sqs_poller_policy), 1, "Only one statement must be in SQS Poller policy") + + sqs_policy_statement = sqs_poller_policy[0] + self.assertTrue(type(sqs_policy_statement["Resource"]) != list) + + queue_url = self.get_physical_id_by_type("AWS::SQS::Queue") + parts = queue_url.split("/") + expected_queue_name = parts[-1] + actual_queue_arn = sqs_policy_statement["Resource"] + self.assertTrue( + actual_queue_arn.endswith(expected_queue_name), + "Queue Arn " + actual_queue_arn + " must end with suffix " + expected_queue_name, + ) + + # Verify the contents of second policy + lambda_invoke_policy = get_policy_statements( + role_name, "MyFunctionRolePolicy1", self.client_provider.iam_client + ) + self.assertEqual(len(lambda_invoke_policy), 1, "One policies statements should be present") + + lambda_policy_statement = lambda_invoke_policy[0] + self.assertTrue(type(lambda_policy_statement["Resource"]) != list) + + # NOTE: The resource ARN has "*" suffix to allow for any Lambda function version as well + expected_function_suffix = "function:somename*" + actual_function_arn = lambda_policy_statement["Resource"] + self.assertTrue( + actual_function_arn.endswith(expected_function_suffix), + "Function ARN " + actual_function_arn + " must end with suffix " + expected_function_suffix, + ) + + # Verify the contents of third policy + cloud_watch_put_metric_policy = get_policy_statements( + role_name, "MyFunctionRolePolicy2", self.client_provider.iam_client + ) + self.assertEqual( + len(cloud_watch_put_metric_policy), 1, "Only one statement must be in CloudWatchPutMetricPolicy" + ) + + cloud_watch_put_metric_statement = cloud_watch_put_metric_policy[0] + self.assertEqual(cloud_watch_put_metric_statement.get("Resource"), "*") + + def test_all_policy_templates(self): + # template too large, upload it to s3 + self.create_and_verify_stack("combination/all_policy_templates", s3_uploader=self.s3_uploader) + + iam_roles = self.get_stack_resources("AWS::IAM::Role") + actual_num_polices = 0 + + for iam_role in iam_roles: + role_name = iam_role.get("PhysicalResourceId") + result = self.client_provider.iam_client.list_role_policies(RoleName=role_name) + policy_names = result.get("PolicyNames") + actual_num_polices += len(policy_names) + + expected_num_polices = 69 + self.assertEqual(actual_num_polices, expected_num_polices) diff --git a/integration/combination/test_function_with_signing_profile.py b/integration/combination/test_function_with_signing_profile.py index b20823ad0..0bcd37353 100644 --- a/integration/combination/test_function_with_signing_profile.py +++ b/integration/combination/test_function_with_signing_profile.py @@ -5,7 +5,7 @@ from integration.config.service_names import CODE_SIGN -class TestDependsOn(BaseTest): +class TestFunctionWithSigningProfile(BaseTest): @skipIf(current_region_does_not_support([CODE_SIGN]), "CodeSign is not supported in this testing region") def test_function_with_signing_profile(self): self.create_and_verify_stack("combination/function_with_signing_profile") diff --git a/integration/combination/test_http_api_with_auth.py b/integration/combination/test_http_api_with_auth.py index 315e9c371..57e76ec66 100644 --- a/integration/combination/test_http_api_with_auth.py +++ b/integration/combination/test_http_api_with_auth.py @@ -55,7 +55,7 @@ def test_function_with_user_pool_event(self): self.assertEqual(oauth_2_auth["IdentitySource"][0], "$request.querystring.param") # Test updating stack - self.update_stack("combination/http_api_with_auth_updated") + self.update_stack(file_path="combination/http_api_with_auth_updated") http_api_list_updated = self.get_stack_resources("AWS::ApiGatewayV2::Api") self.assertEqual(len(http_api_list_updated), 1) diff --git a/integration/combination/test_http_api_with_disable_execute_api_endpoint.py b/integration/combination/test_http_api_with_disable_execute_api_endpoint.py index 35931ba65..5e3bc0221 100644 --- a/integration/combination/test_http_api_with_disable_execute_api_endpoint.py +++ b/integration/combination/test_http_api_with_disable_execute_api_endpoint.py @@ -3,11 +3,11 @@ from parameterized import parameterized from integration.helpers.base_test import BaseTest -from integration.helpers.resource import current_region_does_not_support +from integration.helpers.resource import current_region_not_included from integration.config.service_names import CUSTOM_DOMAIN -@skipIf(current_region_does_not_support([CUSTOM_DOMAIN]), "CustomDomain is not supported in this testing region") +@skipIf(current_region_not_included([CUSTOM_DOMAIN]), "CustomDomain is not supported in this testing region") class TestHttpApiWithDisableExecuteApiEndpoint(BaseTest): @parameterized.expand( [ diff --git a/integration/config/region_service_exclusion.yaml b/integration/config/region_service_exclusion.yaml index 1258a5ef9..a26d185ef 100644 --- a/integration/config/region_service_exclusion.yaml +++ b/integration/config/region_service_exclusion.yaml @@ -9,6 +9,8 @@ regions: - GatewayResponses - HttpApi - ARM + - MSK + - MQ ap-east-1: - Cognito - IoT @@ -26,8 +28,35 @@ regions: - 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: @@ -60,6 +89,11 @@ regions: - GatewayResponses - HttpApi - ARM + - MSK + - MQ + - CodeSign + - CweCwsDlq + - Mode eu-north-1: - ServerlessRepo - Cognito @@ -77,6 +111,8 @@ regions: - GatewayResponses - HttpApi - ARM + - MSK + - MQ eu-west-2: - HttpApi eu-west-3: @@ -98,7 +134,98 @@ regions: - ARM us-east-2: - HttpApi - us-west-1: + us-gov-east-1: + - ServerlessRepo - Cognito + - XRay - IoT - - ARM + - 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 new file mode 100644 index 000000000..14625b38d --- /dev/null +++ b/integration/config/region_service_inclusion.yaml @@ -0,0 +1,3 @@ +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 d29d26d47..6862586a7 100644 --- a/integration/config/service_names.py +++ b/integration/config/service_names.py @@ -20,3 +20,4 @@ SNS = "SNS" SQS = "SQS" CUSTOM_DOMAIN = "CustomDomain" +ARM = "ARM" diff --git a/integration/conftest.py b/integration/conftest.py index 4d6472217..d0a2782ee 100644 --- a/integration/conftest.py +++ b/integration/conftest.py @@ -9,6 +9,7 @@ from integration.helpers.deployer.exceptions.exceptions import 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 try: from pathlib import Path @@ -106,12 +107,54 @@ def get_prefix(request): return prefix +@pytest.fixture() +def get_stage(request): + stage = "" + if request.config.getoption("--stage"): + stage = request.config.getoption("--stage") + return stage + + +@pytest.fixture() +def check_internal(request): + internal = False + if request.config.getoption("--internal"): + internal = True + return internal + + +@pytest.fixture() +def parameter_values(request): + parameter_values = {} + parameter_values_option = request.config.getoption("--parameter-values") + if parameter_values_option: + parameter_values = load_yaml(parameter_values_option) + return parameter_values + + def pytest_addoption(parser): parser.addoption( "--prefix", default=None, + action="store", help="the prefix of the stack", ) + parser.addoption( + "--stage", + default=None, + action="store", + help="the stage of the stack", + ) + parser.addoption( + "--internal", + action="store_true", + help="run internal tests", + ) + parser.addoption( + "--parameter-values", + default=None, + help="YAML file path which will contain parameter values that could be passed during deployment of the stack", + ) @retry_with_exponential_backoff_and_jitter(ThrottlingError, 5, 360) diff --git a/integration/helpers/base_internal_test.py b/integration/helpers/base_internal_test.py new file mode 100644 index 000000000..44be48c5a --- /dev/null +++ b/integration/helpers/base_internal_test.py @@ -0,0 +1,49 @@ +from copy import deepcopy + +import pytest + +from integration.helpers.base_test import BaseTest + + +class BaseInternalTest(BaseTest): + """ + Base class for internal only tests. These tests will skip if --internal flag is not provided + """ + + @pytest.fixture(autouse=True) + def internal(self, check_internal): + self.pipeline_internal = check_internal + + @pytest.fixture(autouse=True) + def template_parameter_values(self, parameter_values): + self.parameter_values = parameter_values + + @pytest.fixture(autouse=True) + def skip_if_not_internal(self): + if not self.pipeline_internal: + pytest.skip("This test is marked as internal but --internal flag is not provided, skipping...") + + def create_and_verify_stack(self, file_path, parameters=None): + super().create_and_verify_stack(file_path, self._get_merged_parameters(parameters)) + + def _get_test_parameters(self): + test_name = self.id().split(".")[-1] + raw_test_parameters = deepcopy(self.parameter_values.get(test_name, {})) + + test_parameters = [] + for parameter_key, parameter_value in raw_test_parameters.items(): + test_parameters.append( + { + "ParameterKey": parameter_key, + "ParameterValue": parameter_value, + "UsePreviousValue": False, + "ResolvedValue": "string", + } + ) + + return test_parameters + + def _get_merged_parameters(self, parameters): + if parameters: + return parameters + self._get_test_parameters() + return self._get_test_parameters() diff --git a/integration/helpers/base_test.py b/integration/helpers/base_test.py index 78f756c8f..785b2af54 100644 --- a/integration/helpers/base_test.py +++ b/integration/helpers/base_test.py @@ -1,6 +1,6 @@ -import json import logging import os +import shutil import botocore import pytest @@ -10,6 +10,7 @@ from integration.helpers.deployer.exceptions.exceptions import ThrottlingError from integration.helpers.deployer.utils.retry import retry_with_exponential_backoff_and_jitter 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 samtranslator.yaml_helper import yaml_parse @@ -36,14 +37,18 @@ class BaseTest(TestCase): def prefix(self, get_prefix): self.pipeline_prefix = get_prefix + @pytest.fixture(autouse=True) + def stage(self, get_stage): + self.pipeline_stage = get_stage + @classmethod - @pytest.mark.usefixtures("get_prefix") + @pytest.mark.usefixtures("get_prefix", "get_stage", "check_internal", "parameter_values") def setUpClass(cls): cls.FUNCTION_OUTPUT = "hello" cls.tests_integ_dir = Path(__file__).resolve().parents[1] cls.resources_dir = Path(cls.tests_integ_dir, "resources") cls.template_dir = Path(cls.resources_dir, "templates") - cls.output_dir = Path(cls.tests_integ_dir, "tmp") + 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() @@ -61,6 +66,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): cls._clean_bucket() + shutil.rmtree(cls.output_dir) @classmethod def _clean_bucket(cls): @@ -126,15 +132,17 @@ def _get_s3_uri(cls, file_name, uri_type): def setUp(self): self.deployer = Deployer(self.client_provider.cfn_client) + self.s3_uploader = S3Uploader(self.client_provider.s3_client, self.s3_bucket_name) def tearDown(self): - self.client_provider.cfn_client.delete_stack(StackName=self.stack_name) - if os.path.exists(self.output_file_path): + if self.stack_name: + self.client_provider.cfn_client.delete_stack(StackName=self.stack_name) + if self.output_file_path and os.path.exists(self.output_file_path): os.remove(self.output_file_path) - if os.path.exists(self.sub_input_file_path): + if self.sub_input_file_path and os.path.exists(self.sub_input_file_path): os.remove(self.sub_input_file_path) - def create_stack(self, file_path, parameters=None): + def create_stack(self, file_path, parameters=None, s3_uploader=None): """ Creates the Cloud Formation stack and verifies it against the expected result @@ -145,6 +153,8 @@ def create_stack(self, file_path, parameters=None): Template file name, format "folder_name/file_name" parameters : list List of parameters + s3_uploader: S3Uploader object + Object for uploading files to s3 """ folder, file_name = file_path.split("/") self.generate_out_put_file_path(folder, file_name) @@ -154,9 +164,9 @@ def create_stack(self, file_path, parameters=None): self._fill_template(folder, file_name) self.transform_template() - self.deploy_stack(parameters) + self._create_stack(parameters, s3_uploader) - def create_and_verify_stack(self, file_path, parameters=None): + def create_and_verify_stack(self, file_path, parameters=None, s3_uploader=None): """ Creates the Cloud Formation stack and verifies it against the expected result @@ -167,57 +177,61 @@ def create_and_verify_stack(self, file_path, parameters=None): Template file name, format "folder_name/file_name" parameters : list List of parameters + s3_uploader: S3Uploader object + Object for uploading files to s3 """ folder, file_name = file_path.split("/") - self.create_stack(file_path, parameters) + + # If template is too large, calling the method with self.s3_uploader to send the template to s3 then deploy + self.create_stack(file_path, parameters, s3_uploader) self.expected_resource_path = str(Path(self.expected_dir, folder, file_name + ".json")) self.verify_stack() - def update_stack(self, file_path, parameters=None): + def update_stack(self, parameters=None, file_path=None): """ Updates the Cloud Formation stack Parameters ---------- - file_path : string - Template file name, format "folder_name/file_name" parameters : list List of parameters + file_path : string + Template file name, format "folder_name/file_name" """ - if os.path.exists(self.output_file_path): - os.remove(self.output_file_path) - if os.path.exists(self.sub_input_file_path): - os.remove(self.sub_input_file_path) + if not self.stack_name: + raise Exception("Stack not created.") - folder, file_name = file_path.split("/") - self.generate_out_put_file_path(folder, file_name) + if file_path: + if os.path.exists(self.output_file_path): + os.remove(self.output_file_path) + if os.path.exists(self.sub_input_file_path): + os.remove(self.sub_input_file_path) + + folder, file_name = file_path.split("/") + self.generate_out_put_file_path(folder, file_name) + + self._fill_template(folder, file_name) - self._fill_template(folder, file_name) self.transform_template() - self.deploy_stack(parameters) + self._update_stack(parameters) - def update_and_verify_stack(self, file_path, parameters=None): + def update_and_verify_stack(self, parameters=None, file_path=None): """ - Updates the Cloud Formation stack and verifies it against the expected + Updates the Cloud Formation stack with new template and verifies it against the expected result Parameters ---------- - file_name : string - Template file name parameters : list List of parameters + file_path : string + Template file name, format "folder_name/file_name" """ - if not self.stack_name: - raise Exception("Stack not created.") + self.update_stack(file_path=file_path, parameters=parameters) folder, file_name = file_path.split("/") self.generate_out_put_file_path(folder, file_name) self.expected_resource_path = str(Path(self.expected_dir, folder, file_name + ".json")) - - self._fill_template(folder, file_name) - self.transform_template() - self.deploy_stack(parameters) self.verify_stack(end_state="UPDATE_COMPLETE") def generate_out_put_file_path(self, folder_name, file_name): @@ -228,7 +242,22 @@ def generate_out_put_file_path(self, folder_name, file_name): ) def transform_template(self): - transform_template(self.sub_input_file_path, self.output_file_path) + if not self.pipeline_stage: + transform_template(self.sub_input_file_path, self.output_file_path) + else: + transform_name = "AWS::Serverless-2016-10-31" + if self.pipeline_stage == "beta": + transform_name = "AWS::Serverless-2016-10-31-Beta" + elif self.pipeline_stage == "gamma": + transform_name = "AWS::Serverless-2016-10-31-Gamma" + elif self.pipeline_stage == "test": + transform_name = "AWS::Serverless-2016-10-31-test" + template = load_yaml(self.sub_input_file_path) + template["AWSTemplateFormatVersion"] = "2010-09-09" + template["Transform"] = transform_name + + dump_yaml(self.output_file_path, template) + print("Wrote transformed CloudFormation template to: " + self.output_file_path) def get_region(self): return self.my_region @@ -421,20 +450,24 @@ def get_template_resource_property(self, resource_name, property_name): yaml_doc = load_yaml(self.sub_input_file_path) return yaml_doc["Resources"][resource_name]["Properties"][property_name] - def deploy_stack(self, parameters=None): - """ - Deploys the current cloud formation stack - """ + def _create_stack(self, parameters=None, s3_uploader=None): + self._create_and_execute_changeset_with_type("CREATE", parameters, s3_uploader) + + def _update_stack(self, parameters=None): + self._create_and_execute_changeset_with_type("UPDATE", parameters) + + def _create_and_execute_changeset_with_type(self, changeset_type, parameters=None, s3_uploader=None): with open(self.output_file_path) as cfn_file: - result, changeset_type = self.deployer.create_and_wait_for_changeset( + result = self.deployer.create_and_wait_for_changeset( stack_name=self.stack_name, cfn_template=cfn_file.read(), parameter_values=[] if parameters is None else parameters, capabilities=["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"], role_arn=None, notification_arns=[], - s3_uploader=None, + s3_uploader=s3_uploader, tags=[], + changeset_type=changeset_type, ) self.deployer.execute_changeset(result["Id"], self.stack_name) self.deployer.wait_for_execute(self.stack_name, changeset_type) diff --git a/integration/helpers/client_provider.py b/integration/helpers/client_provider.py index 5686f4755..25855765a 100644 --- a/integration/helpers/client_provider.py +++ b/integration/helpers/client_provider.py @@ -15,6 +15,7 @@ def __init__(self): self._sfn_client = None self._cloudwatch_log_client = None self._cloudwatch_event_client = None + self._cloudwatch_client = None self._sqs_client = None self._sns_client = None self._dynamoDB_streams_client = None @@ -115,6 +116,16 @@ def cloudwatch_event_client(self): self._cloudwatch_event_client = boto3.client("events") return self._cloudwatch_event_client + @property + def cloudwatch_client(self): + """ + CloudWatch Client + """ + with self._lock: + if not self._cloudwatch_client: + self._cloudwatch_client = boto3.client("cloudwatch") + return self._cloudwatch_client + @property def sqs_client(self): """ diff --git a/integration/helpers/deployer/deployer.py b/integration/helpers/deployer/deployer.py index 3a0c9ff99..63d16cacd 100644 --- a/integration/helpers/deployer/deployer.py +++ b/integration/helpers/deployer/deployer.py @@ -31,6 +31,7 @@ import logging import time from datetime import datetime +from integration.helpers.resource import generate_suffix import botocore @@ -92,49 +93,17 @@ def __init__(self, cloudformation_client, changeset_prefix="sam-integ-"): self.max_attempts = 3 self.deploy_color = DeployColor() - def has_stack(self, stack_name): - """ - Checks if a CloudFormation stack with given name exists - - :param stack_name: Name or ID of the stack - :return: True if stack exists. False otherwise - """ - try: - resp = self._client.describe_stacks(StackName=stack_name) - if not resp["Stacks"]: - return False - - # When you run CreateChangeSet on a a stack that does not exist, - # CloudFormation will create a stack and set it's status - # REVIEW_IN_PROGRESS. However this stack is cannot be manipulated - # by "update" commands. Under this circumstances, we treat like - # this stack does not exist and call CreateChangeSet will - # ChangeSetType set to CREATE and not UPDATE. - stack = resp["Stacks"][0] - return stack["StackStatus"] != "REVIEW_IN_PROGRESS" - - except botocore.exceptions.ClientError as e: - # If a stack does not exist, describe_stacks will throw an - # exception. Unfortunately we don't have a better way than parsing - # the exception msg to understand the nature of this exception. - - if "Stack with id {0} does not exist".format(stack_name) in str(e): - LOG.debug("Stack with id %s does not exist", stack_name) - return False - except botocore.exceptions.BotoCoreError as e: - # If there are credentials, environment errors, - # catch that and throw a deploy failed error. - - LOG.debug("Botocore Exception : %s", str(e)) - raise deploy_exceptions.DeployFailedError(stack_name=stack_name, msg=str(e)) - - except Exception as e: - # We don't know anything about this exception. Don't handle - LOG.debug("Unable to get stack details.", exc_info=e) - raise e - def create_changeset( - self, stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags + self, + stack_name, + cfn_template, + parameter_values, + capabilities, + role_arn, + notification_arns, + s3_uploader, + tags, + changeset_type, ): """ Call Cloudformation to create a changeset and wait for it to complete @@ -144,16 +113,11 @@ def create_changeset( :param parameter_values: Template parameters object :param capabilities: Array of capabilities passed to CloudFormation :param tags: Array of tags passed to CloudFormation + :param tags: the type of the changeset :return: """ - if not self.has_stack(stack_name): - changeset_type = "CREATE" - # When creating a new stack, UsePreviousValue=True is invalid. - # For such parameters, users should either override with new value, - # or set a Default value in template to successfully create a stack. - parameter_values = [x for x in parameter_values if not x.get("UsePreviousValue", False)] - else: - changeset_type = "UPDATE" + + if type == "UPDATE": # UsePreviousValue not valid if parameter is new summary = self._client.get_template_summary(StackName=stack_name) existing_parameters = [parameter["ParameterKey"] for parameter in summary["Parameters"]] @@ -162,6 +126,11 @@ def create_changeset( for x in parameter_values if not (x.get("UsePreviousValue", False) and x["ParameterKey"] not in existing_parameters) ] + else: + # When creating a new stack, UsePreviousValue=True is invalid. + # For such parameters, users should either override with new value, + # or set a Default value in template to successfully create a stack. + parameter_values = [x for x in parameter_values if not x.get("UsePreviousValue", False)] # Each changeset will get a unique name based on time. # Description is also setup based on current date and that SAM CLI is used. @@ -183,11 +152,12 @@ def create_changeset( temporary_file.write(kwargs.pop("TemplateBody")) temporary_file.flush() + # add random suffix to file_name to enable multiple tests run in the same time + template_file_name = "template" + generate_suffix() + # TemplateUrl property requires S3 URL to be in path-style format - parts = parse_s3_url( - s3_uploader.upload_with_dedup(temporary_file.name, "template"), version_property="Version" - ) - kwargs["TemplateURL"] = s3_uploader.to_path_style_s3_url(parts["Key"], parts.get("Version", None)) + s3_uploader.upload_file(template_file_name, temporary_file.name) + kwargs["TemplateURL"] = s3_uploader.get_s3_uri(template_file_name) # don't set these arguments if not specified to use existing values if role_arn is not None: @@ -447,15 +417,32 @@ def _wait(self, stack_name, waiter, waiter_config): raise deploy_exceptions.DeployFailedError(stack_name=stack_name, msg=str(ex)) def create_and_wait_for_changeset( - self, stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags + self, + stack_name, + cfn_template, + parameter_values, + capabilities, + role_arn, + notification_arns, + s3_uploader, + tags, + changeset_type, ): try: result, changeset_type = self.create_changeset( - stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags + stack_name, + cfn_template, + parameter_values, + capabilities, + role_arn, + notification_arns, + s3_uploader, + tags, + changeset_type, ) self.wait_for_changeset(result["Id"], stack_name) self.describe_changeset(result["Id"], stack_name) - return result, changeset_type + return result except botocore.exceptions.ClientError as ex: raise deploy_exceptions.DeployFailedError(stack_name=stack_name, msg=str(ex)) diff --git a/integration/helpers/deployer/utils/table_print.py b/integration/helpers/deployer/utils/table_print.py index 4cc27d6f3..5ab1ac262 100644 --- a/integration/helpers/deployer/utils/table_print.py +++ b/integration/helpers/deployer/utils/table_print.py @@ -2,6 +2,7 @@ Utilities for table pretty printing using click This was ported over from the sam-cli repo """ +import shutil from itertools import count try: @@ -50,7 +51,7 @@ def pprint_column_names(format_string, format_kwargs, margin=None, table_header= def pprint_wrap(func): # Calculate terminal width, number of columns in the table - width, _ = click.get_terminal_size() + width, _ = shutil.get_terminal_size() # For UX purposes, set a minimum width for the table to be usable # and usable_width keeps margins in mind. width = max(width, min_width) diff --git a/integration/helpers/file_resources.py b/integration/helpers/file_resources.py index 5f8aca2ce..5ee533ca8 100644 --- a/integration/helpers/file_resources.py +++ b/integration/helpers/file_resources.py @@ -16,4 +16,5 @@ "definitionuri": "swagger1.json", "templateurl": "template.yaml", "binaryMediaCodeUri": "binary-media.zip", + "mtlsuri": "MTLSCert.pem", } diff --git a/integration/helpers/resource.py b/integration/helpers/resource.py index 6a31d1a77..704d0f3aa 100644 --- a/integration/helpers/resource.py +++ b/integration/helpers/resource.py @@ -131,6 +131,21 @@ def create_bucket(bucket_name, region): s3_client.create_bucket(Bucket=bucket_name, CreateBucketConfiguration=location) +def _get_region(): + """Returns current region from boto3 session object""" + session = boto3.session.Session() + region = session.region_name + return region + + +def _read_test_config_file(filename): + """Reads test inclusion or exclusion 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) + return test_config + + def current_region_does_not_support(services): """ Decide if a test should be skipped in the current testing region with the specific resources @@ -146,13 +161,8 @@ def current_region_does_not_support(services): If skip return true otherwise false """ - session = boto3.session.Session() - region = session.region_name - - tests_integ_dir = Path(__file__).resolve().parents[1] - config_dir = Path(tests_integ_dir, "config") - region_exclude_services_file = str(Path(config_dir, "region_service_exclusion.yaml")) - region_exclude_services = load_yaml(region_exclude_services_file) + region = _get_region() + region_exclude_services = _read_test_config_file("region_service_exclusion.yaml") if region not in region_exclude_services["regions"]: return False @@ -161,6 +171,21 @@ def current_region_does_not_support(services): return bool(set(services).intersection(set(region_exclude_services["regions"][region]))) +def current_region_not_included(services): + """ + Opposite of current_region_does_not_support. + Decides which tests should only be run in certain regions + """ + region = _get_region() + region_include_services = _read_test_config_file("region_service_inclusion.yaml") + + if region not in region_include_services["regions"]: + return True + + # check if any one of the services is in the excluded services for current testing region + return not bool(set(services).intersection(set(region_include_services["regions"][region]))) + + def first_item_in_dict(dictionary): """ return the first key-value pair in dictionary diff --git a/integration/helpers/s3_uploader.py b/integration/helpers/s3_uploader.py new file mode 100644 index 000000000..2afe6f047 --- /dev/null +++ b/integration/helpers/s3_uploader.py @@ -0,0 +1,46 @@ +""" +Client for uploading files to s3 +""" +from typing import Any + +from botocore.exceptions import ClientError +import logging + +LOG = logging.getLogger(__name__) + + +class S3Uploader: + """ + Class to upload objects to S3 bucket. + """ + + def __init__( + self, + s3_client: Any, + bucket_name: str, + ): + self.s3_client = s3_client + self.bucket_name = bucket_name + + def upload_file(self, file_name, file_path): + """ + Uploads given file to S3 + :param file_name: the file name as s3 bucket key + :param file_path: Path to the file that will be uploaded + """ + try: + LOG.debug("Uploading file %s to bucket %s", file_name, self.bucket_name) + self.s3_client.upload_file(Filename=file_path, Bucket=self.bucket_name, Key=file_name) + LOG.debug("File %s uploaded successfully to bucket %s", file_name, self.bucket_name) + except ClientError as error: + LOG.error("Upload of file %s to bucket %s failed", file_name, self.bucket_name, exc_info=error) + raise error + + def get_s3_uri(self, file_name): + """ + This link describes the format of Path Style URLs + http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro + """ + base = self.s3_client.meta.endpoint_url + result = "{0}/{1}/{2}".format(base, self.bucket_name, file_name) + return result diff --git a/integration/helpers/stack.py b/integration/helpers/stack.py index d55faa6ce..1ffafa190 100644 --- a/integration/helpers/stack.py +++ b/integration/helpers/stack.py @@ -41,7 +41,7 @@ def _deploy_stack(self, output_file_path, parameters=None): Deploys the current cloud formation stack """ with open(output_file_path) as cfn_file: - result, changeset_type = self.deployer.create_and_wait_for_changeset( + result = self.deployer.create_and_wait_for_changeset( stack_name=self.stack_name, cfn_template=cfn_file.read(), parameter_values=[] if parameters is None else parameters, @@ -50,9 +50,10 @@ def _deploy_stack(self, output_file_path, parameters=None): notification_arns=[], s3_uploader=None, tags=[], + changeset_type="CREATE", ) self.deployer.execute_changeset(result["Id"], self.stack_name) - self.deployer.wait_for_execute(self.stack_name, changeset_type) + self.deployer.wait_for_execute(self.stack_name, "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_authorizer_apikey.json b/integration/resources/expected/combination/api_with_authorizer_apikey.json new file mode 100644 index 000000000..ecf278698 --- /dev/null +++ b/integration/resources/expected/combination/api_with_authorizer_apikey.json @@ -0,0 +1,15 @@ +[ + { "LogicalResourceId":"MyApi", "ResourceType":"AWS::ApiGateway::RestApi" }, + { "LogicalResourceId":"MyApiDeployment", "ResourceType":"AWS::ApiGateway::Deployment" }, + { "LogicalResourceId":"MyApiMyLambdaTokenAuthAuthorizerPermission", "ResourceType":"AWS::Lambda::Permission" }, + { "LogicalResourceId":"MyApiProdStage", "ResourceType":"AWS::ApiGateway::Stage" }, + { "LogicalResourceId":"MyFirstApiKey", "ResourceType":"AWS::ApiGateway::ApiKey" }, + { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, + { "LogicalResourceId":"MyFunctionLambdaTokenPermissionProd", "ResourceType":"AWS::Lambda::Permission" }, + { "LogicalResourceId":"MyFunctionNonePermissionProd", "ResourceType":"AWS::Lambda::Permission" }, + { "LogicalResourceId":"MyFunctionRole", "ResourceType":"AWS::IAM::Role" }, + { "LogicalResourceId":"MyLambdaAuthFunction", "ResourceType":"AWS::Lambda::Function" }, + { "LogicalResourceId":"MyLambdaAuthFunctionRole", "ResourceType":"AWS::IAM::Role" }, + { "LogicalResourceId":"MyUsagePlan", "ResourceType":"AWS::ApiGateway::UsagePlan" }, + { "LogicalResourceId":"MyUsagePlanKey", "ResourceType":"AWS::ApiGateway::UsagePlanKey" } +] \ No newline at end of file diff --git a/integration/resources/expected/combination/api_with_custom_domains_edge.json b/integration/resources/expected/combination/api_with_custom_domains_edge.json new file mode 100644 index 000000000..b55752b59 --- /dev/null +++ b/integration/resources/expected/combination/api_with_custom_domains_edge.json @@ -0,0 +1,11 @@ +[ + { "LogicalResourceId":"RecordSetGroup", "ResourceType":"AWS::Route53::RecordSetGroup" }, + { "LogicalResourceId":"MyApiDeployment", "ResourceType":"AWS::ApiGateway::Deployment" }, + { "LogicalResourceId":"ApiGatewayDomainName", "ResourceType":"AWS::ApiGateway::DomainName" }, + { "LogicalResourceId":"MyApi", "ResourceType":"AWS::ApiGateway::RestApi" }, + { "LogicalResourceId":"MyApiProdStage", "ResourceType":"AWS::ApiGateway::Stage" }, + { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, + { "LogicalResourceId":"MyFunctionRole", "ResourceType":"AWS::IAM::Role" }, + { "LogicalResourceId":"MyApigetBasePathMapping", "ResourceType":"AWS::ApiGateway::BasePathMapping" }, + { "LogicalResourceId":"MyFunctionFetchPermissionProd", "ResourceType":"AWS::Lambda::Permission" } +] \ No newline at end of file diff --git a/integration/resources/expected/combination/api_with_custom_domains_regional.json b/integration/resources/expected/combination/api_with_custom_domains_regional.json new file mode 100644 index 000000000..3d6e17b45 --- /dev/null +++ b/integration/resources/expected/combination/api_with_custom_domains_regional.json @@ -0,0 +1,13 @@ +[ + { "LogicalResourceId":"MyFunctionImplicitGetPermissionProd", "ResourceType":"AWS::Lambda::Permission" }, + { "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":"ServerlessRestApiProdStage", "ResourceType":"AWS::ApiGateway::Stage" }, + { "LogicalResourceId":"ServerlessRestApigetBasePathMapping", "ResourceType":"AWS::ApiGateway::BasePathMapping" }, + { "LogicalResourceId":"ApiGatewayDomainName", "ResourceType":"AWS::ApiGateway::DomainName" }, + { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, + { "LogicalResourceId":"MyFunctionImplicitPostPermissionProd", "ResourceType":"AWS::Lambda::Permission" }, + { "LogicalResourceId":"MyFunctionRole", "ResourceType":"AWS::IAM::Role" } +] \ No newline at end of file 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 new file mode 100644 index 000000000..3d6e17b45 --- /dev/null +++ b/integration/resources/expected/combination/api_with_custom_domains_regional_ownership_verification.json @@ -0,0 +1,13 @@ +[ + { "LogicalResourceId":"MyFunctionImplicitGetPermissionProd", "ResourceType":"AWS::Lambda::Permission" }, + { "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":"ServerlessRestApiProdStage", "ResourceType":"AWS::ApiGateway::Stage" }, + { "LogicalResourceId":"ServerlessRestApigetBasePathMapping", "ResourceType":"AWS::ApiGateway::BasePathMapping" }, + { "LogicalResourceId":"ApiGatewayDomainName", "ResourceType":"AWS::ApiGateway::DomainName" }, + { "LogicalResourceId":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, + { "LogicalResourceId":"MyFunctionImplicitPostPermissionProd", "ResourceType":"AWS::Lambda::Permission" }, + { "LogicalResourceId":"MyFunctionRole", "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 new file mode 100644 index 000000000..7c7db8ba4 --- /dev/null +++ b/integration/resources/expected/combination/http_api_with_custom_domains_regional_ownership_verification.json @@ -0,0 +1,12 @@ +[ + { "LogicalResourceId":"MyFunctionImplicitGetPermission", "ResourceType":"AWS::Lambda::Permission" }, + { "LogicalResourceId":"MyFunctionImplicitPostPermission", "ResourceType":"AWS::Lambda::Permission" }, + { "LogicalResourceId":"MyApipostApiMapping", "ResourceType":"AWS::ApiGatewayV2::ApiMapping" }, + { "LogicalResourceId":"MyApigetApiMapping", "ResourceType":"AWS::ApiGatewayV2::ApiMapping" }, + { "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":"MyFunction", "ResourceType":"AWS::Lambda::Function" }, + { "LogicalResourceId":"MyFunctionRole", "ResourceType":"AWS::IAM::Role" } +] \ No newline at end of file diff --git a/integration/resources/expected/single/basic_api_with_mode.json b/integration/resources/expected/single/basic_api_with_mode.json index 3f83d75c4..15c83189e 100644 --- a/integration/resources/expected/single/basic_api_with_mode.json +++ b/integration/resources/expected/single/basic_api_with_mode.json @@ -7,5 +7,5 @@ {"LogicalResourceId": "TestFunctionGetPermissionMyNewStageName", "ResourceType": "AWS::Lambda::Permission"}, {"LogicalResourceId": "TestFunctionPutPermissionMyNewStageName", "ResourceType": "AWS::Lambda::Permission"}, {"LogicalResourceId": "TestFunctionRole", "ResourceType": "AWS::IAM::Role"}, - {"LogicalResourceId": "TestFunctionVersione9898fd501", "ResourceType": "AWS::Lambda::Version"} + {"LogicalResourceId": "TestFunctionVersion", "ResourceType": "AWS::Lambda::Version"} ] diff --git a/integration/resources/expected/single/basic_api_with_mode_update.json b/integration/resources/expected/single/basic_api_with_mode_update.json index a862e3e17..cc143fb24 100644 --- a/integration/resources/expected/single/basic_api_with_mode_update.json +++ b/integration/resources/expected/single/basic_api_with_mode_update.json @@ -1,10 +1,10 @@ [ {"LogicalResourceId": "MyApi", "ResourceType": "AWS::ApiGateway::RestApi"}, - {"LogicalResourceId": "MyApiDeploymentb30ecf9df1", "ResourceType": "AWS::ApiGateway::Deployment"}, + {"LogicalResourceId": "MyApiDeployment", "ResourceType": "AWS::ApiGateway::Deployment"}, {"LogicalResourceId": "MyApiMyNewStageNameStage", "ResourceType": "AWS::ApiGateway::Stage"}, {"LogicalResourceId": "TestFunction", "ResourceType": "AWS::Lambda::Function"}, {"LogicalResourceId": "TestFunctionAliaslive", "ResourceType": "AWS::Lambda::Alias"}, {"LogicalResourceId": "TestFunctionPutPermissionMyNewStageName", "ResourceType": "AWS::Lambda::Permission"}, {"LogicalResourceId": "TestFunctionRole", "ResourceType": "AWS::IAM::Role"}, - {"LogicalResourceId": "TestFunctionVersion847aaa5fc1", "ResourceType": "AWS::Lambda::Version"} + {"LogicalResourceId": "TestFunctionVersion", "ResourceType": "AWS::Lambda::Version"} ] diff --git a/integration/resources/expected/single/function_with_http_api_events_and_auth.json b/integration/resources/expected/single/function_with_http_api_events_and_auth.json new file mode 100644 index 000000000..b505f2126 --- /dev/null +++ b/integration/resources/expected/single/function_with_http_api_events_and_auth.json @@ -0,0 +1,62 @@ +[ + { + "LogicalResourceId": "MyDefaultIamAuthHttpApi", + "ResourceType": "AWS::ApiGatewayV2::Api" + }, + { + "LogicalResourceId": "MyDefaultIamAuthHttpApiApiGatewayDefaultStage", + "ResourceType": "AWS::ApiGatewayV2::Stage" + }, + { + "LogicalResourceId": "MyIamAuthEnabledHttpApi", + "ResourceType": "AWS::ApiGatewayV2::Api" + }, + { + "LogicalResourceId": "MyIamAuthEnabledHttpApiApiGatewayDefaultStage", + "ResourceType": "AWS::ApiGatewayV2::Stage" + }, + { + "LogicalResourceId": "MyLambdaFunction", + "ResourceType": "AWS::Lambda::Function" + }, + { + "LogicalResourceId": "MyLambdaFunctionImplicitApiDefaultAuthEventPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionImplicitApiIamAuthEventPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionMyDefaultIamAuthHttpApiDefaultAuthEventPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionMyDefaultIamAuthHttpApiIamAuthEventPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionMyDefaultIamAuthHttpApiNoAuthEventPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionMyIamAuthEnabledHttpApiDefaultAuthEventPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionMyIamAuthEnabledHttpApiIamAuthEventPermission", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionRole", + "ResourceType": "AWS::IAM::Role" + }, + { + "LogicalResourceId": "ServerlessHttpApi", + "ResourceType": "AWS::ApiGatewayV2::Api" + }, + { + "LogicalResourceId": "ServerlessHttpApiApiGatewayDefaultStage", + "ResourceType": "AWS::ApiGatewayV2::Stage" + } +] diff --git a/integration/resources/templates/combination/api_with_authorizer_apikey.yaml b/integration/resources/templates/combination/api_with_authorizer_apikey.yaml new file mode 100644 index 000000000..e820ed2a2 --- /dev/null +++ b/integration/resources/templates/combination/api_with_authorizer_apikey.yaml @@ -0,0 +1,116 @@ +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + ApiKeySourceType: AUTHORIZER + StageName: Prod + Auth: + Authorizers: + MyLambdaTokenAuth: + FunctionArn: + Fn::GetAtt: MyLambdaAuthFunction.Arn + + MyFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event, context, callback) => { + return { + statusCode: 200, + body: 'Success' + } + } + Events: + None: + Type: Api + Properties: + RestApiId: + Ref: MyApi + Method: get + Path: /none + LambdaToken: + Type: Api + Properties: + RestApiId: + Ref: MyApi + Method: get + Path: /lambda-token-api-key + Auth: + Authorizer: MyLambdaTokenAuth + ApiKeyRequired: true + + MyLambdaAuthFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event, context, callback) => { + console.log(event); + const token = event.type === 'TOKEN' ? event.authorizationToken : event.queryStringParameters.authorization + const policyDocument = { + Version: '2012-10-17', + Statement: [{ + Action: 'execute-api:Invoke', + Effect: token && token.toLowerCase() === 'allow' ? 'Allow' : 'Deny', + Resource: event.methodArn + }] + } + + return { + principalId: 'user', + usageIdentifierKey: 'at_least_20_characters', + context: { }, + policyDocument + } + } + + MyFirstApiKey: + Type: AWS::ApiGateway::ApiKey + DependsOn: + - MyUsagePlan + Properties: + Enabled: true + Value: at_least_20_characters + StageKeys: + - RestApiId: + Ref: MyApi + StageName: + Ref: MyApi.Stage + + MyUsagePlan: + Type: AWS::ApiGateway::UsagePlan + DependsOn: + - MyApi + Properties: + ApiStages: + - ApiId: + Ref: MyApi + Stage: + Ref: MyApi.Stage + + MyUsagePlanKey: + Type: AWS::ApiGateway::UsagePlanKey + Properties: + KeyId: + Ref: MyFirstApiKey + UsagePlanId: + Ref: MyUsagePlan + KeyType: API_KEY + +Outputs: + ApiKeyId: + Description: "API Key ID" + Value: + Fn::GetAtt: MyFirstApiKey.APIKeyId + ApiUrl: + Description: "API endpoint URL for Prod environment" + Value: + Fn::Sub: 'https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/Prod/' + + AuthorizerFunctionArn: + Description: "Authorizer Function Arn" + Value: + Fn::GetAtt: MyLambdaAuthFunction.Arn diff --git a/integration/resources/templates/combination/api_with_custom_domains_edge.yaml b/integration/resources/templates/combination/api_with_custom_domains_edge.yaml new file mode 100644 index 000000000..042909bbf --- /dev/null +++ b/integration/resources/templates/combination/api_with_custom_domains_edge.yaml @@ -0,0 +1,45 @@ +Parameters: + MyEdgeDomainName: + Type: String + MyEdgeDomainCert: + Type: String + +Resources: + MyFunction: + 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: + Fetch: + Type: Api + Properties: + RestApiId: + Ref: MyApi + Method: Get + Path: /get + + MyApi: + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: Prod + Domain: + DomainName: + Ref: MyEdgeDomainName + CertificateArn: + Ref: MyEdgeDomainCert + EndpointConfiguration: EDGE + BasePath: + - /get + Route53: + HostedZoneId: Z1SKZDMQ2UR7IW + IpV6: true \ No newline at end of file diff --git a/integration/resources/templates/combination/api_with_custom_domains_regional.yaml b/integration/resources/templates/combination/api_with_custom_domains_regional.yaml new file mode 100644 index 000000000..4e811b585 --- /dev/null +++ b/integration/resources/templates/combination/api_with_custom_domains_regional.yaml @@ -0,0 +1,49 @@ +Parameters: + MyRestRegionalDomainName: + Type: String + MyRestRegionalDomainCert: + Type: String + +Globals: + Api: + Domain: + DomainName: + Ref: MyRestRegionalDomainName + CertificateArn: + Ref: MyRestRegionalDomainCert + EndpointConfiguration: REGIONAL + MutualTlsAuthentication: + TruststoreUri: ${mtlsuri} + TruststoreVersion: 0 + SecurityPolicy: TLS_1_2 + BasePath: + - /get + - /post + Route53: + HostedZoneId: Z1DTV8GVAVOHDR + +Resources: + MyFunction: + 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: + ImplicitGet: + Type: Api + Properties: + Method: Get + Path: /get + ImplicitPost: + Type: Api + Properties: + Method: Post + Path: /post \ 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 new file mode 100644 index 000000000..7c1fe5498 --- /dev/null +++ b/integration/resources/templates/combination/api_with_custom_domains_regional_ownership_verification.yaml @@ -0,0 +1,53 @@ +Parameters: + MyDomainName: + Type: String + MyDomainCertificate: + Type: String + MyDomainOwnershipVerificationCertificate: + Type: String + +Globals: + Api: + Domain: + DomainName: + Ref: MyDomainName + CertificateArn: + arn:aws:acm:us-east-1:830899278857:certificate/7c9b4aa7-4262-4b20-85c0-ec45d8d55fa6 + EndpointConfiguration: REGIONAL + MutualTlsAuthentication: + TruststoreUri: ${mtlsuri} + TruststoreVersion: 0 + SecurityPolicy: TLS_1_2 + BasePath: + - /get + - /post + Route53: + HostedZoneId: Z1DTV8GVAVOHDR + OwnershipVerificationCertificateArn: + Fn::Sub: arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/798c74e5-dc55-48b6-b1d8-13d2d7b3d265 + +Resources: + MyFunction: + 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: + ImplicitGet: + Type: Api + Properties: + Method: Get + Path: /get + ImplicitPost: + Type: Api + Properties: + Method: Post + Path: /post diff --git a/integration/resources/templates/combination/function_with_custom_code_deploy.yaml b/integration/resources/templates/combination/function_with_custom_code_deploy.yaml index d2f9e550b..a3d24e118 100644 --- a/integration/resources/templates/combination/function_with_custom_code_deploy.yaml +++ b/integration/resources/templates/combination/function_with_custom_code_deploy.yaml @@ -60,4 +60,4 @@ Resources: Type: TimeBasedLinear TimeBasedLinear: LinearInterval: 1 - LinearPercentage: 50 + LinearPercentage: 50 \ No newline at end of file diff --git a/integration/resources/templates/combination/http_api_with_custom_domains_regional.yaml b/integration/resources/templates/combination/http_api_with_custom_domains_regional.yaml index 443251fc6..000b0c30c 100644 --- a/integration/resources/templates/combination/http_api_with_custom_domains_regional.yaml +++ b/integration/resources/templates/combination/http_api_with_custom_domains_regional.yaml @@ -1,10 +1,8 @@ Parameters: MyDomainName: Type: String - Default: httpapi.sam-gamma-regional.com MyDomainCert: Type: String - Default: arn:aws:acm:us-east-1:830899278857:certificate/ae8c894b-4e41-42a6-8817-85d05665d043 Globals: HttpApi: diff --git a/integration/resources/templates/combination/http_api_with_custom_domains_regional_ownership_verification.yaml b/integration/resources/templates/combination/http_api_with_custom_domains_regional_ownership_verification.yaml new file mode 100644 index 000000000..e0e55ead8 --- /dev/null +++ b/integration/resources/templates/combination/http_api_with_custom_domains_regional_ownership_verification.yaml @@ -0,0 +1,61 @@ +Parameters: + MyRegionalDomainName: + Type: String + MyRegionalDomainCert: + Type: String + MyOwnershipVerificationCert: + Type: String + +Globals: + HttpApi: + Domain: + DomainName: + Ref: MyRegionalDomainName + CertificateArn: + Ref: MyRegionalDomainCert + EndpointConfiguration: REGIONAL + MutualTlsAuthentication: + TruststoreUri: ${mtlsuri} + TruststoreVersion: 0 + SecurityPolicy: TLS_1_2 + BasePath: + - /get + - /post + Route53: + HostedZoneId: Z1DTV8GVAVOHDR + OwnershipVerificationCertificateArn: + Ref: MyOwnershipVerificationCert + +Resources: + MyFunction: + 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: + ImplicitGet: + Type: HttpApi + Properties: + Method: Get + Path: /get + ApiId: + Ref: MyApi + ImplicitPost: + Type: HttpApi + Properties: + Method: Post + Path: /post + ApiId: + Ref: MyApi + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + StageName: Prod diff --git a/integration/resources/templates/single/function_with_http_api_events_and_auth.yaml b/integration/resources/templates/single/function_with_http_api_events_and_auth.yaml new file mode 100644 index 000000000..bbff00e73 --- /dev/null +++ b/integration/resources/templates/single/function_with_http_api_events_and_auth.yaml @@ -0,0 +1,96 @@ +Globals: + HttpApi: + Auth: + EnableIamAuthorizer: true +Resources: + ####### + # Serverless function that use the implicit AWS::Serverless::HttpApi called "ServerlessHttpApi". + # IAM Authorizer of the implicit AWS::Serverless::HttpApi is enabled using the global above. + ####### + MyLambdaFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + CodeUri: ${codeuri} + Events: + # The following events use the implicit AWS::Serverless::HttpApi called "ServerlessHttpApi". + # The Iam Authorizer of the implicit AWS::Serverless::HttpApi is enabled using the global above. + # Should not have any auth enabled because there is no one set as the default. + ImplicitApiDefaultAuthEvent: + Type: HttpApi + Properties: + Path: /default-auth + Method: GET + # Should have Iam auth as it is set here. + ImplicitApiIamAuthEvent: + Type: HttpApi + Properties: + Auth: + Authorizer: AWS_IAM + Path: /iam-auth + Method: GET + + # The following events use the defined AWS::Serverless::HttpApi further down. + # Should not have any auth enabled. + MyDefaultIamAuthHttpApiNoAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyDefaultIamAuthHttpApi + Auth: + Authorizer: NONE + Path: /no-auth + Method: GET + # Should have Iam auth as it is set as the default for the Api. + MyDefaultIamAuthHttpApiDefaultAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyDefaultIamAuthHttpApi + Path: /default-auth + Method: GET + # Should have Iam auth as it is set here. + MyDefaultIamAuthHttpApiIamAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyDefaultIamAuthHttpApi + Auth: + Authorizer: AWS_IAM + Path: /iam-auth + Method: GET + # The following events use the defined AWS::Serverless::HttpApi further down. + # Should not have any auth enabled because there is no one set as the default. + MyIamAuthEnabledHttpApiDefaultAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyIamAuthEnabledHttpApi + Path: /default-auth + Method: GET + # Should have Iam auth as it is set here. + MyIamAuthEnabledHttpApiIamAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyIamAuthEnabledHttpApi + Auth: + Authorizer: AWS_IAM + Path: /iam-auth + Method: GET + + # HTTP API resource with the Iam authorizer enabled and set to the default. + MyDefaultIamAuthHttpApi: + Type: AWS::Serverless::HttpApi + Properties: + Auth: + EnableIamAuthorizer: true + DefaultAuthorizer: AWS_IAM + + # HTTP API resource with the Iam authorizer enabled and NOT set to the default. + MyIamAuthEnabledHttpApi: + Type: AWS::Serverless::HttpApi + Properties: + Auth: + EnableIamAuthorizer: true diff --git a/integration/single/test_basic_api.py b/integration/single/test_basic_api.py index e8de40556..9eb00ecb8 100644 --- a/integration/single/test_basic_api.py +++ b/integration/single/test_basic_api.py @@ -23,8 +23,7 @@ def test_basic_api(self): self.assertEqual(len(first_dep_ids), 1) self.set_template_resource_property("MyApi", "DefinitionUri", self.get_s3_uri("swagger2.json")) - self.transform_template() - self.deploy_stack() + self.update_stack() second_dep_ids = self.get_stack_deployment_ids() self.assertEqual(len(second_dep_ids), 1) @@ -45,8 +44,8 @@ def test_basic_api_with_mode(self): self.assertEqual(response.status_code, 200) # Removes get from the API - self.update_and_verify_stack("single/basic_api_with_mode_update") - + self.update_and_verify_stack(file_path="single/basic_api_with_mode_update") + response = requests.get(f"{api_endpoint}/get") # API Gateway by default returns 403 if a path do not exist retries = 20 while retries > 0: @@ -70,8 +69,7 @@ def test_basic_api_inline_openapi(self): body = self.get_template_resource_property("MyApi", "DefinitionBody") body["basePath"] = "/newDemo" self.set_template_resource_property("MyApi", "DefinitionBody", body) - self.transform_template() - self.deploy_stack() + self.update_stack() second_dep_ids = self.get_stack_deployment_ids() self.assertEqual(len(second_dep_ids), 1) @@ -90,8 +88,7 @@ def test_basic_api_inline_swagger(self): body = self.get_template_resource_property("MyApi", "DefinitionBody") body["basePath"] = "/newDemo" self.set_template_resource_property("MyApi", "DefinitionBody", body) - self.transform_template() - self.deploy_stack() + self.update_stack() second_dep_ids = self.get_stack_deployment_ids() self.assertEqual(len(second_dep_ids), 1) diff --git a/integration/single/test_basic_function.py b/integration/single/test_basic_function.py index 92c703a92..951d47826 100644 --- a/integration/single/test_basic_function.py +++ b/integration/single/test_basic_function.py @@ -2,6 +2,7 @@ import requests +from integration.config.service_names import KMS, XRAY, ARM from integration.helpers.resource import current_region_does_not_support from parameterized import parameterized from integration.helpers.base_test import BaseTest @@ -26,8 +27,7 @@ def test_basic_function(self, file_name): self.create_and_verify_stack(file_name) self.set_template_resource_property("MyLambdaFunction", "Timeout", 10) - self.transform_template() - self.deploy_stack() + self.update_stack() self.assertEqual(self.get_resource_status_by_logical_id("MyLambdaFunction"), "UPDATE_COMPLETE") @@ -53,7 +53,7 @@ def test_function_with_http_api_events(self, file_name): ("single/basic_function_with_x86_architecture", ["x86_64"]), ] ) - @skipIf(current_region_does_not_support(["ARM"]), "ARM is not supported in this testing region") + @skipIf(current_region_does_not_support([ARM]), "ARM is not supported in this testing region") def test_basic_function_with_architecture(self, file_name, architecture): """ Creates a basic lambda function @@ -98,6 +98,7 @@ def test_basic_function_with_url_config(self, file_name, qualifier): self.assertEqual(function_url_config["AuthType"], "NONE") self.assertEqual(function_url_config["Cors"], cors_config) + self._assert_invoke(lambda_client, function_name, qualifier, 200) def test_function_with_deployment_preference_alarms_intrinsic_if(self): self.create_and_verify_stack("single/function_with_deployment_preference_alarms_intrinsic_if") @@ -133,7 +134,7 @@ def test_basic_function_with_dlq(self, file_name, action): self.assertEqual(statements[0]["Resource"], dlq_arn) self.assertEqual(statements[0]["Effect"], "Allow") - @skipIf(current_region_does_not_support(["KMS"]), "KMS is not supported in this testing region") + @skipIf(current_region_does_not_support([KMS]), "KMS is not supported in this testing region") def test_basic_function_with_kms_key_arn(self): """ Creates a basic lambda function with KMS key arn @@ -208,7 +209,7 @@ def test_basic_function_event_destinations(self): "MaximumRetryAttempts value is not set or incorrect.", ) - @skipIf(current_region_does_not_support(["XRay"]), "XRay is not supported in this testing region") + @skipIf(current_region_does_not_support([XRAY]), "XRay is not supported in this testing region") def test_basic_function_with_tracing(self): """ Creates a basic lambda function with tracing @@ -256,3 +257,28 @@ def test_function_with_ephemeral_storage(self, file_name): ) self.assertEqual(function_configuration_result.get("EphemeralStorage", {}).get("Size", 0), 1024) + + def _assert_invoke(self, lambda_client, function_name, qualifier=None, expected_status_code=200): + """ + Assert if a Lambda invocation returns the expected status code + + Parameters + ---------- + lambda_client : boto3.BaseClient + boto3 Lambda client + function_name : string + Function name + qualifier : string + Specify a version or alias to invoke a published version of the function + expected_status_code : int + Expected status code from the invocation + """ + request_params = { + "FunctionName": function_name, + "Payload": "{}", + } + if qualifier: + request_params["Qualifier"] = qualifier + + response = lambda_client.invoke(**request_params) + self.assertEqual(response.get("StatusCode"), expected_status_code) diff --git a/integration/single/test_basic_layer_version.py b/integration/single/test_basic_layer_version.py index fbddf5de4..cdba80ffd 100644 --- a/integration/single/test_basic_layer_version.py +++ b/integration/single/test_basic_layer_version.py @@ -1,16 +1,17 @@ from unittest.case import skipIf +from integration.config.service_names import LAYERS, ARM from integration.helpers.base_test import BaseTest from integration.helpers.resource import current_region_does_not_support +@skipIf(current_region_does_not_support([LAYERS]), "Layers is not supported in this testing region") class TestBasicLayerVersion(BaseTest): """ Basic AWS::Lambda::LayerVersion tests """ - @skipIf(current_region_does_not_support(["Layers"]), "Layers is not supported in this testing region") - def test_basic_layer_version(self, filename): + def test_basic_layer_version(self): """ Creates a basic lambda layer version """ @@ -19,14 +20,13 @@ def test_basic_layer_version(self, filename): layer_logical_id_1 = self.get_logical_id_by_type("AWS::Lambda::LayerVersion") self.set_template_resource_property("MyLayerVersion", "Description", "A basic layer") - self.transform_template() - self.deploy_stack() + + self.update_stack() layer_logical_id_2 = self.get_logical_id_by_type("AWS::Lambda::LayerVersion") self.assertFalse(layer_logical_id_1 == layer_logical_id_2) - @skipIf(current_region_does_not_support(["Layers"]), "Layers is not supported in this testing region") def test_basic_layer_with_parameters(self): """ Creates a basic lambda layer version with parameters @@ -47,8 +47,7 @@ def test_basic_layer_with_parameters(self): self.assertEqual(layer_version_result["LicenseInfo"], license) self.assertEqual(layer_version_result["Description"], description) - @skipIf(current_region_does_not_support(["Layers"]), "Layers is not supported in this testing region") - @skipIf(current_region_does_not_support(["ARM"]), "ARM is not supported in this testing region") + @skipIf(current_region_does_not_support([ARM]), "ARM is not supported in this testing region") def test_basic_layer_with_architecture(self): """ Creates a basic lambda layer version specifying compatible architecture diff --git a/integration/single/test_function_with_http_api_and_auth.py b/integration/single/test_function_with_http_api_and_auth.py new file mode 100644 index 000000000..c1e5546d2 --- /dev/null +++ b/integration/single/test_function_with_http_api_and_auth.py @@ -0,0 +1,29 @@ +import requests +from parameterized import parameterized +from integration.helpers.base_test import BaseTest + + +class TestFunctionWithHttpApiAndAuth(BaseTest): + """ + AWS::Lambda::Function tests with http api events and auth + """ + + def test_function_with_http_api_and_auth(self): + # If the request is not signed, which none of the below are, IAM will respond with a "Forbidden" message. + # 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") + + implicitEndpoint = self.get_api_v2_endpoint("ServerlessHttpApi") + self.assertEqual(requests.get(implicitEndpoint + "/default-auth").text, self.FUNCTION_OUTPUT) + self.assertEqual(requests.get(implicitEndpoint + "/iam-auth").text, IAM_AUTH_OUTPUT) + + defaultIamEndpoint = self.get_api_v2_endpoint("MyDefaultIamAuthHttpApi") + self.assertEqual(requests.get(defaultIamEndpoint + "/no-auth").text, self.FUNCTION_OUTPUT) + self.assertEqual(requests.get(defaultIamEndpoint + "/default-auth").text, IAM_AUTH_OUTPUT) + self.assertEqual(requests.get(defaultIamEndpoint + "/iam-auth").text, IAM_AUTH_OUTPUT) + + iamEnabledEndpoint = self.get_api_v2_endpoint("MyIamAuthEnabledHttpApi") + self.assertEqual(requests.get(iamEnabledEndpoint + "/default-auth").text, self.FUNCTION_OUTPUT) + self.assertEqual(requests.get(iamEnabledEndpoint + "/iam-auth").text, IAM_AUTH_OUTPUT) diff --git a/requirements/base.txt b/requirements/base.txt index 060ee4318..5a3e77e5d 100755 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,2 +1,2 @@ -boto3~=1.5 +boto3>=1.19.5,==1.* jsonschema~=3.2 diff --git a/requirements/dev.txt b/requirements/dev.txt index a2dcac6e7..1c674a098 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -14,7 +14,7 @@ parameterized~=0.7.4 # Integration tests click~=7.1 dateparser~=0.7 -boto3~=1.17 +boto3>=1.23,<2 # Requirements for examples requests~=2.24.0 diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index e8f5c6071..a29c76c70 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.45.0" +__version__ = "1.46.0" diff --git a/samtranslator/feature_toggle/feature_toggle.py b/samtranslator/feature_toggle/feature_toggle.py index 5f18513eb..0f6f239ce 100644 --- a/samtranslator/feature_toggle/feature_toggle.py +++ b/samtranslator/feature_toggle/feature_toggle.py @@ -13,9 +13,6 @@ ) from samtranslator.metrics.method_decorator import cw_timer -my_path = os.path.dirname(os.path.abspath(__file__)) -sys.path.insert(0, my_path + "/..") - LOG = logging.getLogger(__name__) diff --git a/samtranslator/model/__init__.py b/samtranslator/model/__init__.py index ca0efc59f..add5367ef 100644 --- a/samtranslator/model/__init__.py +++ b/samtranslator/model/__init__.py @@ -327,7 +327,7 @@ def get_runtime_attr(self, attr_name): if attr_name in self.runtime_attrs: return self.runtime_attrs[attr_name](self) else: - raise NotImplementedError(attr_name + " attribute is not implemented for resource " + self.resource_type) + raise NotImplementedError(f"{attr_name} attribute is not implemented for resource {self.resource_type}") def get_passthrough_resource_attributes(self): """ @@ -447,7 +447,7 @@ def _check_tag(self, reserved_tag_name, tags): if reserved_tag_name in tags: raise InvalidResourceException( self.logical_id, - reserved_tag_name + " is a reserved Tag key name and " + f"{reserved_tag_name} is a reserved Tag key name and " "cannot be set on your resource. " "Please change the tag key in the " "input.", diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 806c55450..92b6b9fb6 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -183,6 +183,7 @@ def __init__( domain=None, description=None, mode=None, + api_key_source_type=None, ): """Constructs an API Generator class that generates API Gateway resources @@ -235,6 +236,7 @@ def __init__( self.shared_api_usage_plan = shared_api_usage_plan self.template_conditions = template_conditions self.mode = mode + self.api_key_source_type = api_key_source_type def _construct_rest_api(self): """Constructs and returns the ApiGateway RestApi. @@ -294,6 +296,9 @@ def _construct_rest_api(self): if self.mode: rest_api.Mode = self.mode + if self.api_key_source_type: + rest_api.ApiKeySourceType = self.api_key_source_type + return rest_api def _add_endpoint_extension(self): diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py index d033a75bd..869dddec4 100644 --- a/samtranslator/model/api/http_api_generator.py +++ b/samtranslator/model/api/http_api_generator.py @@ -24,8 +24,8 @@ ) CorsProperties.__new__.__defaults__ = (None, None, None, None, None, False) -AuthProperties = namedtuple("_AuthProperties", ["Authorizers", "DefaultAuthorizer"]) -AuthProperties.__new__.__defaults__ = (None, None) +AuthProperties = namedtuple("_AuthProperties", ["Authorizers", "DefaultAuthorizer", "EnableIamAuthorizer"]) +AuthProperties.__new__.__defaults__ = (None, None, False) DefaultStageName = "$default" HttpApiTagName = "httpapi:createdBy" @@ -422,7 +422,7 @@ def _add_auth(self): ) open_api_editor = OpenApiEditor(self.definition_body) auth_properties = AuthProperties(**self.auth) - authorizers = self._get_authorizers(auth_properties.Authorizers, auth_properties.DefaultAuthorizer) + authorizers = self._get_authorizers(auth_properties.Authorizers, auth_properties.EnableIamAuthorizer) # authorizers is guaranteed to return a value or raise an exception open_api_editor.add_authorizers_security_definitions(authorizers) @@ -494,14 +494,21 @@ def _set_default_authorizer(self, open_api_editor, authorizers, default_authoriz path, default_authorizer, authorizers=authorizers, api_authorizers=api_authorizers ) - def _get_authorizers(self, authorizers_config, default_authorizer=None): + def _get_authorizers(self, authorizers_config, enable_iam_authorizer=False): """ Returns all authorizers for an API as an ApiGatewayV2Authorizer object :param authorizers_config: authorizer configuration from the API Auth section - :param default_authorizer: name of the default authorizer + :param enable_iam_authorizer: if True add an "AWS_IAM" authorizer """ authorizers = {} + if enable_iam_authorizer is True: + authorizers["AWS_IAM"] = ApiGatewayV2Authorizer(is_aws_iam_authorizer=True) + + # If all the customer wants to do is enable the IAM authorizer the authorizers_config will be None. + if not authorizers_config: + return authorizers + if not isinstance(authorizers_config, dict): raise InvalidResourceException(self.logical_id, "Authorizers must be a dictionary.") diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index a987e5a99..3b48c8c6b 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -24,6 +24,7 @@ class ApiGatewayRestApi(Resource): "BinaryMediaTypes": PropertyType(False, is_type(list)), "MinimumCompressionSize": PropertyType(False, is_type(int)), "Mode": PropertyType(False, is_str()), + "ApiKeySourceType": PropertyType(False, is_str()), } runtime_attrs = {"rest_api_id": lambda self: ref(self.logical_id)} @@ -247,13 +248,13 @@ def __init__( if function_payload_type not in ApiGatewayAuthorizer._VALID_FUNCTION_PAYLOAD_TYPES: raise InvalidResourceException( api_logical_id, - name + " Authorizer has invalid " "'FunctionPayloadType': " + function_payload_type + ".", + f"{name} Authorizer has invalid 'FunctionPayloadType': {function_payload_type}.", ) if function_payload_type == "REQUEST" and self._is_missing_identity_source(identity): raise InvalidResourceException( api_logical_id, - name + " Authorizer must specify Identity with at least one " + f"{name} Authorizer must specify Identity with at least one " "of Headers, QueryStrings, StageVariables, or Context.", ) diff --git a/samtranslator/model/apigatewayv2.py b/samtranslator/model/apigatewayv2.py index a9976a1be..14f00b63d 100644 --- a/samtranslator/model/apigatewayv2.py +++ b/samtranslator/model/apigatewayv2.py @@ -73,6 +73,7 @@ def __init__( identity=None, authorizer_payload_format_version=None, enable_simple_responses=None, + is_aws_iam_authorizer=False, ): """ Creates an authorizer for use in V2 Http Apis @@ -87,6 +88,7 @@ def __init__( self.identity = identity self.authorizer_payload_format_version = authorizer_payload_format_version self.enable_simple_responses = enable_simple_responses + self.is_aws_iam_authorizer = is_aws_iam_authorizer self._validate_input_parameters() @@ -100,6 +102,8 @@ def __init__( self._validate_lambda_authorizer() def _get_auth_type(self): + if self.is_aws_iam_authorizer: + return "AWS_IAM" if self.jwt_configuration: return "JWT" return "REQUEST" @@ -151,26 +155,26 @@ def _validate_input_parameters(self): def _validate_jwt_authorizer(self): if not self.jwt_configuration: raise InvalidResourceException( - self.api_logical_id, self.name + " OAuth2 Authorizer must define 'JwtConfiguration'." + self.api_logical_id, f"{self.name} OAuth2 Authorizer must define 'JwtConfiguration'." ) if not self.id_source: raise InvalidResourceException( - self.api_logical_id, self.name + " OAuth2 Authorizer must define 'IdentitySource'." + self.api_logical_id, f"{self.name} OAuth2 Authorizer must define 'IdentitySource'." ) def _validate_lambda_authorizer(self): if not self.function_arn: raise InvalidResourceException( - self.api_logical_id, self.name + " Lambda Authorizer must define 'FunctionArn'." + self.api_logical_id, f"{self.name} Lambda Authorizer must define 'FunctionArn'." ) if not self.authorizer_payload_format_version: raise InvalidResourceException( - self.api_logical_id, self.name + " Lambda Authorizer must define 'AuthorizerPayloadFormatVersion'." + self.api_logical_id, f"{self.name} Lambda Authorizer must define 'AuthorizerPayloadFormatVersion'." ) if self.identity and not isinstance(self.identity, dict): raise InvalidResourceException( - self.api_logical_id, self.name + " Lambda Authorizer property 'identity' is of invalid type." + self.api_logical_id, f"{self.name} Lambda Authorizer property 'identity' is of invalid type." ) def generate_openapi(self): @@ -179,6 +183,14 @@ def generate_openapi(self): """ authorizer_type = self._get_auth_type() + if authorizer_type == "AWS_IAM": + openapi = { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4", + } + if authorizer_type == "JWT": openapi = {"type": "oauth2"} openapi[APIGATEWAY_AUTHORIZER_KEY] = { diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index d8d8b8570..4ed685582 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -188,6 +188,13 @@ def _validate_filter_criteria(self): if list(self.FilterCriteria.keys()) not in [[], ["Filters"]]: raise InvalidEventException(self.relative_id, "FilterCriteria field has a wrong format") + def validate_secrets_manager_kms_key_id(self): + if self.SecretsManagerKmsKeyId and not isinstance(self.SecretsManagerKmsKeyId, str): + raise InvalidEventException( + self.relative_id, + "Provided SecretsManagerKmsKeyId should be of type str.", + ) + class Kinesis(PullEventSource): """Kinesis event source.""" @@ -304,6 +311,7 @@ def get_policy_statements(self): }, } if self.SecretsManagerKmsKeyId: + self.validate_secrets_manager_kms_key_id() kms_policy = { "Action": "kms:Decrypt", "Effect": "Allow", @@ -367,6 +375,7 @@ def generate_policy_document(self): statements.append(vpc_permissions) if self.SecretsManagerKmsKeyId: + self.validate_secrets_manager_kms_key_id() kms_policy = self.get_kms_policy() statements.append(kms_policy) diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index b9a6f5f7b..751e0e40b 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -5,7 +5,7 @@ from samtranslator.model import ResourceMacro, PropertyType from samtranslator.model.eventsources import FUNCTION_EVETSOURCE_METRIC_PREFIX from samtranslator.model.types import is_type, list_of, dict_of, one_of, is_str -from samtranslator.model.intrinsics import ref, fnGetAtt, fnSub, make_shorthand, make_conditional +from samtranslator.model.intrinsics import is_intrinsic, ref, fnGetAtt, fnSub, make_shorthand, make_conditional from samtranslator.model.tags.resource_tagging import get_tag_list from samtranslator.model.s3 import S3Bucket @@ -19,7 +19,7 @@ from samtranslator.model.cognito import CognitoUserPool from samtranslator.translator import logical_id_generator from samtranslator.translator.arn_generator import ArnGenerator -from samtranslator.model.exceptions import InvalidEventException, InvalidResourceException +from samtranslator.model.exceptions import InvalidEventException, InvalidResourceException, InvalidDocumentException from samtranslator.swagger.swagger import SwaggerEditor from samtranslator.open_api.open_api import OpenApiEditor from samtranslator.utils.py27hash_fix import Py27Dict, Py27UniStr @@ -705,7 +705,7 @@ def _add_swagger_integration(self, api, function, intrinsics_resolver): ) _check_valid_authorizer_types( - self.relative_id, self.Method, self.Path, method_authorizer, api_authorizers + self.relative_id, self.Method, self.Path, method_authorizer, api_authorizers, False ) if method_authorizer != "NONE" and not api_authorizers.get(method_authorizer): @@ -767,6 +767,14 @@ def _add_swagger_integration(self, api, function, intrinsics_resolver): model=method_model, method=self.Method, path=self.Path ), ) + if not is_intrinsic(api_models) and not isinstance(api_models, dict): + raise InvalidEventException( + self.relative_id, + "Unable to set RequestModel [{model}] on API method [{method}] for path [{path}] " + "because the related API Models defined is of invalid type.".format( + model=method_model, method=self.Method, path=self.Path + ), + ) if not isinstance(method_model, str): raise InvalidEventException( self.relative_id, @@ -1111,7 +1119,7 @@ def _get_permission(self, resources_to_link, stage): if resources_to_link["explicit_api"].get("DefinitionBody"): try: editor = OpenApiEditor(resources_to_link["explicit_api"].get("DefinitionBody")) - except ValueError as e: + except InvalidDocumentException as e: api_logical_id = self.ApiId.get("Ref") if isinstance(self.ApiId, dict) else self.ApiId raise InvalidResourceException(api_logical_id, e) @@ -1209,40 +1217,51 @@ def _add_auth_to_openapi_integration(self, api, editor): # Default auth should already be applied, so apply any other auth here or scope override to default api_authorizers = api_auth and api_auth.get("Authorizers") - _check_valid_authorizer_types(self.relative_id, self.Method, self.Path, method_authorizer, api_authorizers) + # The IAM authorizer is built-in and not defined as a regular Authorizer. + iam_authorizer_enabled = api_auth and api_auth.get("EnableIamAuthorizer", False) is True - if method_authorizer != "NONE" and not api_authorizers: - raise InvalidEventException( - self.relative_id, - "Unable to set Authorizer [{authorizer}] on API method [{method}] for path [{path}] " - "because the related API does not define any Authorizers.".format( - authorizer=method_authorizer, method=self.Method, path=self.Path - ), - ) + _check_valid_authorizer_types( + self.relative_id, self.Method, self.Path, method_authorizer, api_authorizers, iam_authorizer_enabled + ) - if method_authorizer != "NONE" and not api_authorizers.get(method_authorizer): - raise InvalidEventException( - self.relative_id, - "Unable to set Authorizer [{authorizer}] on API method [{method}] for path [{path}] " - "because it wasn't defined in the API's Authorizers.".format( - authorizer=method_authorizer, method=self.Method, path=self.Path - ), - ) + if method_authorizer == "NONE": + if not api_auth.get("DefaultAuthorizer"): + raise InvalidEventException( + self.relative_id, + "Unable to set Authorizer on API method [{method}] for path [{path}] because 'NONE' " + "is only a valid value when a DefaultAuthorizer on the API is specified.".format( + method=self.Method, path=self.Path + ), + ) + # If the method authorizer is "AWS_IAM" but it's not enabled it's possible that's a custom authorizer, not the "official" one. + # In that case a check needs to be performed to make sure that such an authorizer is defined. + # The "official" AWS IAM authorizer is not defined as a normal authorizer so it won't exist in api_authorizer. + elif (method_authorizer == "AWS_IAM" and not iam_authorizer_enabled) or method_authorizer != "AWS_IAM": + if not api_authorizers: + raise InvalidEventException( + self.relative_id, + "Unable to set Authorizer [{authorizer}] on API method [{method}] for path [{path}] " + "because the related API does not define any Authorizers.".format( + authorizer=method_authorizer, method=self.Method, path=self.Path + ), + ) + + if not api_authorizers.get(method_authorizer): + raise InvalidEventException( + self.relative_id, + "Unable to set Authorizer [{authorizer}] on API method [{method}] for path [{path}] " + "because it wasn't defined in the API's Authorizers.".format( + authorizer=method_authorizer, method=self.Method, path=self.Path + ), + ) - if method_authorizer == "NONE" and not api_auth.get("DefaultAuthorizer"): - raise InvalidEventException( - self.relative_id, - "Unable to set Authorizer on API method [{method}] for path [{path}] because 'NONE' " - "is only a valid value when a DefaultAuthorizer on the API is specified.".format( - method=self.Method, path=self.Path - ), - ) if self.Auth.get("AuthorizationScopes") and not isinstance(self.Auth.get("AuthorizationScopes"), list): raise InvalidEventException( self.relative_id, "Unable to set Authorizer on API method [{method}] for path [{path}] because " "'AuthorizationScopes' must be a list of strings.".format(method=self.Method, path=self.Path), ) + editor.add_auth_to_method(api=api, path=self.Path, method_name=self.Method, auth=self.Auth) @@ -1262,12 +1281,19 @@ def _build_apigw_integration_uri(function, partition): return Py27Dict(fnSub(arn)) -def _check_valid_authorizer_types(relative_id, method, path, method_authorizer, api_authorizers): +def _check_valid_authorizer_types( + relative_id, method, path, method_authorizer, api_authorizers, iam_authorizer_enabled +): if method_authorizer == "NONE": # If the method authorizer is "NONE" then this check # isn't needed since DefaultAuthorizer needs to be used. return + if method_authorizer == "AWS_IAM" and iam_authorizer_enabled: + # The "official" AWS IAM authorizer is not defined as a normal authorizer so it won't exist in api_authorizers. + # So we can safety skip this check. + return + if not isinstance(method_authorizer, str) or not isinstance(api_authorizers, dict): raise InvalidEventException( relative_id, diff --git a/samtranslator/model/preferences/deployment_preference.py b/samtranslator/model/preferences/deployment_preference.py index 0b2026d5d..0f876dcdc 100644 --- a/samtranslator/model/preferences/deployment_preference.py +++ b/samtranslator/model/preferences/deployment_preference.py @@ -25,7 +25,16 @@ """ DeploymentPreferenceTuple = namedtuple( "DeploymentPreferenceTuple", - ["deployment_type", "pre_traffic_hook", "post_traffic_hook", "alarms", "enabled", "role", "trigger_configurations"], + [ + "deployment_type", + "pre_traffic_hook", + "post_traffic_hook", + "alarms", + "enabled", + "role", + "trigger_configurations", + "condition", + ], ) @@ -37,17 +46,18 @@ class DeploymentPreference(DeploymentPreferenceTuple): """ @classmethod - def from_dict(cls, logical_id, deployment_preference_dict): + def from_dict(cls, logical_id, deployment_preference_dict, condition=None): """ :param logical_id: the logical_id of the resource that owns this deployment preference :param deployment_preference_dict: the dict object taken from the SAM template + :param condition: condition on this deployment preference :return: """ enabled = deployment_preference_dict.get("Enabled", True) enabled = False if enabled in ["false", "False"] else enabled if not enabled: - return DeploymentPreference(None, None, None, None, False, None, None) + return DeploymentPreference(None, None, None, None, False, None, None, None) if "Type" not in deployment_preference_dict: raise InvalidResourceException(logical_id, "'DeploymentPreference' is missing required Property 'Type'") @@ -64,6 +74,15 @@ def from_dict(cls, logical_id, deployment_preference_dict): alarms = deployment_preference_dict.get("Alarms", None) role = deployment_preference_dict.get("Role", None) trigger_configurations = deployment_preference_dict.get("TriggerConfigurations", None) + passthrough_condition = deployment_preference_dict.get("PassthroughCondition", False) + return DeploymentPreference( - deployment_type, pre_traffic_hook, post_traffic_hook, alarms, enabled, role, trigger_configurations + deployment_type, + pre_traffic_hook, + post_traffic_hook, + alarms, + enabled, + role, + trigger_configurations, + condition if passthrough_condition else None, ) diff --git a/samtranslator/model/preferences/deployment_preference_collection.py b/samtranslator/model/preferences/deployment_preference_collection.py index 5f1c13029..bd5be65f9 100644 --- a/samtranslator/model/preferences/deployment_preference_collection.py +++ b/samtranslator/model/preferences/deployment_preference_collection.py @@ -9,6 +9,9 @@ is_intrinsic_if, is_intrinsic_no_value, validate_intrinsic_if_items, + make_combined_condition, + ref, + fnGetAtt, ) from samtranslator.model.update_policy import UpdatePolicy from samtranslator.translator.arn_generator import ArnGenerator @@ -27,6 +30,7 @@ "Linear10PercentEvery10Minutes", "AllAtOnce", ] +CODE_DEPLOY_CONDITION_NAME = "ServerlessCodeDeployCondition" class DeploymentPreferenceCollection(object): @@ -41,20 +45,19 @@ class DeploymentPreferenceCollection(object): def __init__(self): """ - This collection stores an intenral dict of the deployment preferences for each function's - deployment preference in the SAM Template. + This collection stores an internal dict of the deployment preferences for each function's + deployment preference in the SAM Template. """ self._resource_preferences = {} - self.codedeploy_application = self._codedeploy_application() - self.codedeploy_iam_role = self._codedeploy_iam_role() - def add(self, logical_id, deployment_preference_dict): + def add(self, logical_id, deployment_preference_dict, condition=None): """ Add this deployment preference to the collection :raise ValueError if an existing logical id already exists in the _resource_preferences :param logical_id: logical id of the resource where this deployment preference applies :param deployment_preference_dict: the input SAM template deployment preference mapping + :param condition: the condition (if it exists) on the serverless function """ if logical_id in self._resource_preferences: raise ValueError( @@ -63,7 +66,9 @@ def add(self, logical_id, deployment_preference_dict): ) ) - self._resource_preferences[logical_id] = DeploymentPreference.from_dict(logical_id, deployment_preference_dict) + self._resource_preferences[logical_id] = DeploymentPreference.from_dict( + logical_id, deployment_preference_dict, condition + ) def get(self, logical_id): """ @@ -85,18 +90,52 @@ def can_skip_service_role(self): """ return all(preference.role or not preference.enabled for preference in self._resource_preferences.values()) + def needs_resource_condition(self): + """ + If all preferences have a condition, all code deploy resources need to be conditionally created + :return: True, if a condition needs to be created + """ + # If there are any enabled deployment preferences without conditions, return false + return self._resource_preferences and not any( + not preference.condition and preference.enabled for preference in self._resource_preferences.values() + ) + + def get_all_deployment_conditions(self): + """ + Returns a list of all conditions associated with the deployment preference resources + :return: List of condition names + """ + conditions_set = set([preference.condition for preference in self._resource_preferences.values()]) + if None in conditions_set: + # None can exist if there are disabled deployment preference(s) + conditions_set.remove(None) + return list(conditions_set) + + def create_aggregate_deployment_condition(self): + """ + Creates an aggregate deployment condition if necessary + :return: None if <2 conditions are found, otherwise a dictionary of new conditions to add to template + """ + return make_combined_condition(self.get_all_deployment_conditions(), CODE_DEPLOY_CONDITION_NAME) + def enabled_logical_ids(self): """ :return: only the logical id's for the deployment preferences in this collection which are enabled """ return [logical_id for logical_id, preference in self._resource_preferences.items() if preference.enabled] - def _codedeploy_application(self): + def get_codedeploy_application(self): codedeploy_application_resource = CodeDeployApplication(CODEDEPLOY_APPLICATION_LOGICAL_ID) codedeploy_application_resource.ComputePlatform = "Lambda" + if self.needs_resource_condition(): + conditions = self.get_all_deployment_conditions() + condition_name = CODE_DEPLOY_CONDITION_NAME + if len(conditions) <= 1: + condition_name = conditions.pop() + codedeploy_application_resource.set_resource_attribute("Condition", condition_name) return codedeploy_application_resource - def _codedeploy_iam_role(self): + def get_codedeploy_iam_role(self): iam_role = IAMRole(CODE_DEPLOY_SERVICE_ROLE_LOGICAL_ID) iam_role.AssumeRolePolicyDocument = { "Version": "2012-10-17", @@ -120,6 +159,12 @@ def _codedeploy_iam_role(self): ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSCodeDeployRoleForLambda") ] + if self.needs_resource_condition(): + conditions = self.get_all_deployment_conditions() + condition_name = CODE_DEPLOY_CONDITION_NAME + if len(conditions) <= 1: + condition_name = conditions.pop() + iam_role.set_resource_attribute("Condition", condition_name) return iam_role def deployment_group(self, function_logical_id): @@ -137,7 +182,7 @@ def deployment_group(self, function_logical_id): except ValueError as e: raise InvalidResourceException(function_logical_id, str(e)) - deployment_group.ApplicationName = self.codedeploy_application.get_runtime_attr("name") + deployment_group.ApplicationName = ref(CODEDEPLOY_APPLICATION_LOGICAL_ID) deployment_group.AutoRollbackConfiguration = { "Enabled": True, "Events": ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST"], @@ -149,13 +194,16 @@ def deployment_group(self, function_logical_id): deployment_group.DeploymentStyle = {"DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL"} - deployment_group.ServiceRoleArn = self.codedeploy_iam_role.get_runtime_attr("arn") + deployment_group.ServiceRoleArn = fnGetAtt(CODE_DEPLOY_SERVICE_ROLE_LOGICAL_ID, "Arn") if deployment_preference.role: deployment_group.ServiceRoleArn = deployment_preference.role if deployment_preference.trigger_configurations: deployment_group.TriggerConfigurations = deployment_preference.trigger_configurations + if deployment_preference.condition: + deployment_group.set_resource_attribute("Condition", deployment_preference.condition) + return deployment_group def _convert_alarms(self, preference_alarms): @@ -240,7 +288,7 @@ def update_policy(self, function_logical_id): deployment_preference = self.get(function_logical_id) return UpdatePolicy( - self.codedeploy_application.get_runtime_attr("name"), + ref(CODEDEPLOY_APPLICATION_LOGICAL_ID), self.deployment_group(function_logical_id).get_runtime_attr("name"), deployment_preference.pre_traffic_hook, deployment_preference.post_traffic_hook, diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index dfb1ced4a..d098c23d3 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -1,5 +1,7 @@ """ SAM macro definitions """ import copy +from typing import Union +from samtranslator.intrinsics.resolver import IntrinsicsResolver import samtranslator.model.eventsources import samtranslator.model.eventsources.pull @@ -139,6 +141,7 @@ def to_cloudformation(self, **kwargs): intrinsics_resolver = kwargs["intrinsics_resolver"] mappings_resolver = kwargs.get("mappings_resolver", None) conditions = kwargs.get("conditions", {}) + feature_toggle = kwargs.get("feature_toggle") if self.DeadLetterQueue: self._validate_dlq() @@ -175,7 +178,7 @@ def to_cloudformation(self, **kwargs): if self.FunctionUrlConfig: lambda_url = self._construct_function_url(lambda_function, lambda_alias) resources.append(lambda_url) - url_permission = self._construct_url_permission(lambda_function) + url_permission = self._construct_url_permission(lambda_function, lambda_alias) if url_permission: resources.append(url_permission) @@ -185,6 +188,8 @@ def to_cloudformation(self, **kwargs): lambda_alias, intrinsics_resolver, mappings_resolver, + self.get_passthrough_resource_attributes(), + feature_toggle, ) event_invoke_policies = [] if self.EventInvokeConfig: @@ -827,10 +832,16 @@ def _construct_alias(self, name, function, version): return alias def _validate_deployment_preference_and_add_update_policy( - self, deployment_preference_collection, lambda_alias, intrinsics_resolver, mappings_resolver + self, + deployment_preference_collection, + lambda_alias, + intrinsics_resolver, + mappings_resolver, + passthrough_resource_attributes, + feature_toggle=None, ): if "Enabled" in self.DeploymentPreference: - # resolve intrinsics and mappings for Type + # resolve intrinsics and mappings for Enabled enabled = self.DeploymentPreference["Enabled"] enabled = intrinsics_resolver.resolve_parameter_refs(enabled) enabled = mappings_resolver.resolve_parameter_refs(enabled) @@ -843,10 +854,28 @@ def _validate_deployment_preference_and_add_update_policy( preference_type = mappings_resolver.resolve_parameter_refs(preference_type) self.DeploymentPreference["Type"] = preference_type + if "PassthroughCondition" in self.DeploymentPreference: + self.DeploymentPreference["PassthroughCondition"] = self._resolve_property_to_boolean( + self.DeploymentPreference["PassthroughCondition"], + "PassthroughCondition", + intrinsics_resolver, + mappings_resolver, + ) + elif feature_toggle: + self.DeploymentPreference["PassthroughCondition"] = feature_toggle.is_enabled( + "deployment_preference_condition_fix" + ) + else: + self.DeploymentPreference["PassthroughCondition"] = False + if deployment_preference_collection is None: raise ValueError("deployment_preference_collection required for parsing the deployment preference") - deployment_preference_collection.add(self.logical_id, self.DeploymentPreference) + deployment_preference_collection.add( + self.logical_id, + self.DeploymentPreference, + passthrough_resource_attributes.get("Condition"), + ) if deployment_preference_collection.get(self.logical_id).enabled: if self.AutoPublishAlias is None: @@ -860,6 +889,40 @@ def _validate_deployment_preference_and_add_update_policy( "UpdatePolicy", deployment_preference_collection.update_policy(self.logical_id).to_dict() ) + def _resolve_property_to_boolean( + self, + property_value: Union[bool, str, dict], + property_name: str, + intrinsics_resolver: IntrinsicsResolver, + mappings_resolver: IntrinsicsResolver, + ) -> bool: + """ + Resolves intrinsics, if any, and/or converts string in a given property to boolean. + Raises InvalidResourceException if can't resolve intrinsic or can't resolve string to boolean + + :param property_value: property value to resolve + :param property_name: name/key of property to resolve + :param intrinsics_resolver: resolves intrinsics + :param mappings_resolver: resolves FindInMap + :return bool: resolved boolean value + """ + processed_property_value = intrinsics_resolver.resolve_parameter_refs(property_value) + processed_property_value = mappings_resolver.resolve_parameter_refs(processed_property_value) + + # FIXME: We should support not only true/false, but also yes/no, on/off? See https://yaml.org/type/bool.html + if processed_property_value in [True, "true", "True"]: + return True + elif processed_property_value in [False, "false", "False"]: + return False + elif is_intrinsic(processed_property_value): # couldn't resolve intrinsic + raise InvalidResourceException( + self.logical_id, + f"Unsupported intrinsic: the only intrinsic functions supported for " + f"property {property_name} are FindInMap and parameter Refs.", + ) + else: + raise InvalidResourceException(self.logical_id, f"Invalid value for property {property_name}.") + def _construct_function_url(self, lambda_function, lambda_alias): """ This method is used to construct a lambda url resource @@ -879,7 +942,8 @@ def _construct_function_url(self, lambda_function, lambda_alias): self._validate_function_url_params(lambda_function) logical_id = f"{lambda_function.logical_id}Url" - lambda_url = LambdaUrl(logical_id=logical_id) + lambda_url_attributes = self.get_passthrough_resource_attributes() + lambda_url = LambdaUrl(logical_id=logical_id, attributes=lambda_url_attributes) cors = self.FunctionUrlConfig.get("Cors") if cors: @@ -942,7 +1006,7 @@ def _validate_cors_config_parameter(self, lambda_function): "{} must be of type {}.".format(prop_name, str(prop_type).split("'")[1]), ) - def _construct_url_permission(self, lambda_function): + def _construct_url_permission(self, lambda_function, lambda_alias): """ Construct the lambda permission associated with the function url resource in a case for public access when AuthType is NONE @@ -952,6 +1016,9 @@ def _construct_url_permission(self, lambda_function): lambda_function : LambdaUrl Lambda Function resource + llambda_alias : LambdaAlias + Lambda Alias resource + Returns ------- LambdaPermission @@ -963,9 +1030,12 @@ def _construct_url_permission(self, lambda_function): return None logical_id = f"{lambda_function.logical_id}UrlPublicPermissions" - lambda_permission = LambdaPermission(logical_id=logical_id) + lambda_permission_attributes = self.get_passthrough_resource_attributes() + lambda_permission = LambdaPermission(logical_id=logical_id, attributes=lambda_permission_attributes) lambda_permission.Action = "lambda:InvokeFunctionUrl" - lambda_permission.FunctionName = lambda_function.get_runtime_attr("name") + lambda_permission.FunctionName = ( + lambda_alias.get_runtime_attr("arn") if lambda_alias else lambda_function.get_runtime_attr("name") + ) lambda_permission.Principal = "*" lambda_permission.FunctionUrlAuthType = auth_type return lambda_permission @@ -1006,6 +1076,7 @@ class SamApi(SamResourceMacro): "Description": PropertyType(False, is_str()), "Mode": PropertyType(False, is_str()), "DisableExecuteApiEndpoint": PropertyType(False, is_type(bool)), + "ApiKeySourceType": PropertyType(False, is_str()), } referable_properties = { @@ -1067,6 +1138,7 @@ def to_cloudformation(self, **kwargs): domain=self.Domain, description=self.Description, mode=self.Mode, + api_key_source_type=self.ApiKeySourceType, ) ( diff --git a/samtranslator/open_api/open_api.py b/samtranslator/open_api/open_api.py index 7c7bb5826..2226e27ea 100644 --- a/samtranslator/open_api/open_api.py +++ b/samtranslator/open_api/open_api.py @@ -37,10 +37,10 @@ def __init__(self, doc): modifications on this copy. :param dict doc: OpenApi document as a dictionary - :raises ValueError: If the input OpenApi document does not meet the basic OpenApi requirements. + :raises InvalidDocumentException: If the input OpenApi document does not meet the basic OpenApi requirements. """ if not OpenApiEditor.is_valid(doc): - raise ValueError( + raise InvalidDocumentException( "Invalid OpenApi document. " "Invalid values or missing keys for 'openapi' or 'paths' in 'DefinitionBody'." ) @@ -175,7 +175,7 @@ def add_path(self, path, method=None): :param string path: Path name :param string method: HTTP method - :raises ValueError: If the value of `path` in Swagger is not a dictionary + :raises InvalidDocumentException: If the value of `path` in Swagger is not a dictionary """ method = self._normalize_method_name(method) @@ -455,7 +455,8 @@ def _set_method_authorizer(self, path, method_name, authorizer_name, authorizers security_dict = dict() security_dict[authorizer_name] = [] - if authorizer_name != "NONE": + # Neither the NONE nor the AWS_IAM built-in authorizers support authorization scopes. + if authorizer_name not in ["NONE", "AWS_IAM"]: method_authorization_scopes = authorizers[authorizer_name].get("AuthorizationScopes") if authorization_scopes: method_authorization_scopes = authorization_scopes diff --git a/samtranslator/plugins/__init__.py b/samtranslator/plugins/__init__.py index 320910028..2a565993f 100644 --- a/samtranslator/plugins/__init__.py +++ b/samtranslator/plugins/__init__.py @@ -1,152 +1,10 @@ import logging -from samtranslator.model.exceptions import InvalidResourceException, InvalidDocumentException from enum import Enum LOG = logging.getLogger(__name__) -class SamPlugins(object): - """ - Class providing support for arbitrary plugins that can extend core SAM translator in interesting ways. - Use this class to register plugins that get called when certain life cycle events happen in the translator. - Plugins work only on resources that are natively supported by SAM (ie. AWS::Serverless::* resources) - - Following Life Cycle Events are available: - - **Resource Level** - - before_transform_resource: Invoked before SAM translator processes a resource's properties. - - [Coming Soon] after_transform_resource - - **Template Level** - - before_transform_template - - after_transform_template - - When a life cycle event happens in the translator, this class will invoke the corresponding "hook" method on the - each of the registered plugins to process. Plugins are free to modify internal state of the template or resources - as they see fit. They can even raise an exception when the resource or template doesn't contain properties - of certain structure (Ex: Only PolicyTemplates are allowed in SAM template) - - ## Plugin Implementation - - ### Defining a plugin - A plugin is a subclass of `BasePlugin` that implements one or more methods capable of processing the life cycle - events. - These methods have a prefix `on_` followed by the name of the life cycle event. For example, to handle - `before_transform_resource` event, implement a method called `on_before_transform_resource`. We call these methods - as "hooks" which are methods capable of handling this event. - - ### Hook Methods - Arguments passed to the hook method is different for each life cycle event. Check out the hook methods in the - `BasePlugin` class for detailed description of the method signature - - ### Raising validation errors - Plugins must raise an `samtranslator.model.exception.InvalidResourceException` when the input SAM template does - not conform to the expectation - set by the plugin. SAM translator will convert this into a nice error message and display to the user. - """ - - def __init__(self, initial_plugins=None): - """ - Initialize the plugins class with an optional list of plugins - - :param BasePlugin or list initial_plugins: Single plugin or a List of plugins to initialize with - """ - self._plugins = [] - - if initial_plugins is None: - initial_plugins = [] - - if not isinstance(initial_plugins, list): - initial_plugins = [initial_plugins] - - for plugin in initial_plugins: - self.register(plugin) - - def register(self, plugin): - """ - Register a plugin. New plugins are added to the end of the plugins list. - - :param samtranslator.plugins.BasePlugin plugin: Instance/subclass of BasePlugin class that implements hooks - :raises ValueError: If plugin is not an instance of samtranslator.plugins.BasePlugin or if it is already - registered - :return: None - """ - - if not plugin or not isinstance(plugin, BasePlugin): - raise ValueError("Plugin must be implemented as a subclass of BasePlugin class") - - if self.is_registered(plugin.name): - raise ValueError("Plugin with name {} is already registered".format(plugin.name)) - - self._plugins.append(plugin) - - def is_registered(self, plugin_name): - """ - Checks if a plugin with given name is already registered - - :param plugin_name: Name of the plugin - :return: True if plugin with given name is already registered. False, otherwise - """ - - return plugin_name in [p.name for p in self._plugins] - - def _get(self, plugin_name): - """ - Retrieves the plugin with given name - - :param plugin_name: Name of the plugin to retrieve - :return samtranslator.plugins.BasePlugin: Returns the plugin object if found. None, otherwise - """ - - for p in self._plugins: - if p.name == plugin_name: - return p - - return None - - def act(self, event, *args, **kwargs): - """ - Act on the specific life cycle event. The action here is to invoke the hook function on all registered plugins. - *args and **kwargs will be passed directly to the plugin's hook functions - - :param samtranslator.plugins.LifeCycleEvents event: Event to act upon - :return: Nothing - :raises ValueError: If event is not a valid life cycle event - :raises NameError: If a plugin does not have the hook method defined - :raises Exception: Any exception that a plugin raises - """ - - if not isinstance(event, LifeCycleEvents): - raise ValueError("'event' must be an instance of LifeCycleEvents class") - - method_name = "on_" + event.name - - for plugin in self._plugins: - - if not hasattr(plugin, method_name): - raise NameError( - "'{}' method is not found in the plugin with name '{}'".format(method_name, plugin.name) - ) - - try: - getattr(plugin, method_name)(*args, **kwargs) - except (InvalidResourceException, InvalidDocumentException) as ex: - # Don't need to log these because they don't result in crashes - raise ex - except Exception as ex: - LOG.exception("Plugin '%s' raised an exception: %s", plugin.name, ex) - raise ex - - def __len__(self): - """ - Returns the number of plugins registered with this class - - :return integer: Number of plugins registered - """ - return len(self._plugins) - - class LifeCycleEvents(Enum): """ Enum of LifeCycleEvents diff --git a/samtranslator/plugins/api/implicit_api_plugin.py b/samtranslator/plugins/api/implicit_api_plugin.py index 99397a9c6..9c14fcbcb 100644 --- a/samtranslator/plugins/api/implicit_api_plugin.py +++ b/samtranslator/plugins/api/implicit_api_plugin.py @@ -189,10 +189,8 @@ def _add_api_to_swagger(self, event_id, event_properties, template): if isinstance(api_id, dict) or is_referencing_http_from_api_event: raise InvalidEventException( event_id, - self.api_id_property - + " must be a valid reference to an '" - + self._get_api_resource_type_name() - + "' resource in same template.", + f"{self.api_id_property} must be a valid reference to an '{self._get_api_resource_type_name()}'" + " resource in same template.", ) # Make sure Swagger is valid diff --git a/samtranslator/plugins/api/implicit_http_api_plugin.py b/samtranslator/plugins/api/implicit_http_api_plugin.py index 8ea3a6cd0..78cb2a7bf 100644 --- a/samtranslator/plugins/api/implicit_http_api_plugin.py +++ b/samtranslator/plugins/api/implicit_http_api_plugin.py @@ -58,7 +58,7 @@ def _process_api_events( # api_events only contains HttpApi events event_properties = event.get("Properties", {}) - if event_properties and not isinstance(event_properties, dict): + if not isinstance(event_properties, dict): raise InvalidEventException( logicalId, "Event 'Properties' must be an Object. If you're using YAML, this may be an indentation issue.", diff --git a/samtranslator/plugins/application/serverless_app_plugin.py b/samtranslator/plugins/application/serverless_app_plugin.py index 5b1131e3b..32058291c 100644 --- a/samtranslator/plugins/application/serverless_app_plugin.py +++ b/samtranslator/plugins/application/serverless_app_plugin.py @@ -63,6 +63,7 @@ def __init__(self, sar_client=None, wait_for_template_active_status=False, valid self._wait_for_template_active_status = wait_for_template_active_status self._validate_only = validate_only self._parameters = parameters + self._total_wait_time = 0 # make sure the flag combination makes sense if self._validate_only is True and self._wait_for_template_active_status is True: @@ -118,11 +119,31 @@ def on_before_transform_template(self, template_dict): # Lazy initialization of the client- create it when it is needed if not self._sar_client: self._sar_client = boto3.client("serverlessrepo") - service_call(app_id, semver, key, logical_id) + self._make_service_call_with_retry(service_call, app_id, semver, key, logical_id) except InvalidResourceException as e: # Catch all InvalidResourceExceptions, raise those in the before_resource_transform target. self._applications[key] = e + def _make_service_call_with_retry(self, service_call, app_id, semver, key, logical_id): + call_succeeded = False + while self._total_wait_time < self.TEMPLATE_WAIT_TIMEOUT_SECONDS: + try: + service_call(app_id, semver, key, logical_id) + except ClientError as e: + error_code = e.response["Error"]["Code"] + if error_code == "TooManyRequestsException": + LOG.debug("SAR call timed out for application id {}".format(app_id)) + sleep_time = self._get_sleep_time_sec() + sleep(sleep_time) + self._total_wait_time += sleep_time + continue + else: + raise e + call_succeeded = True + break + if not call_succeeded: + raise InvalidResourceException(logical_id, "Failed to call SAR, timeout limit exceeded.") + def _replace_value(self, input_dict, key, intrinsic_resolvers): value = self._resolve_location_value(input_dict.get(key), intrinsic_resolvers) input_dict[key] = value @@ -307,8 +328,7 @@ def on_after_transform_template(self, template): if not self._wait_for_template_active_status or self._validate_only: return - start_time = time() - while (time() - start_time) < self.TEMPLATE_WAIT_TIMEOUT_SECONDS: + while self._total_wait_time < self.TEMPLATE_WAIT_TIMEOUT_SECONDS: # Check each resource to make sure it's active LOG.info("Checking resources in serverless application repo...") idx = 0 @@ -341,7 +361,9 @@ def on_after_transform_template(self, template): break # Sleep a little so we don't spam service calls - sleep(self._get_sleep_time_sec()) + sleep_time = self._get_sleep_time_sec() + sleep(sleep_time) + self._total_wait_time += sleep_time # Not all templates reached active status if len(self._in_progress_templates) != 0: diff --git a/samtranslator/plugins/sam_plugins.py b/samtranslator/plugins/sam_plugins.py new file mode 100644 index 000000000..98a55263b --- /dev/null +++ b/samtranslator/plugins/sam_plugins.py @@ -0,0 +1,146 @@ +import logging +from samtranslator.model.exceptions import InvalidResourceException, InvalidDocumentException, InvalidTemplateException +from samtranslator.plugins import BasePlugin, LifeCycleEvents + +LOG = logging.getLogger(__name__) + + +class SamPlugins(object): + """ + Class providing support for arbitrary plugins that can extend core SAM translator in interesting ways. + Use this class to register plugins that get called when certain life cycle events happen in the translator. + Plugins work only on resources that are natively supported by SAM (ie. AWS::Serverless::* resources) + + Following Life Cycle Events are available: + + **Resource Level** + - before_transform_resource: Invoked before SAM translator processes a resource's properties. + - [Coming Soon] after_transform_resource + + **Template Level** + - before_transform_template + - after_transform_template + + When a life cycle event happens in the translator, this class will invoke the corresponding "hook" method on the + each of the registered plugins to process. Plugins are free to modify internal state of the template or resources + as they see fit. They can even raise an exception when the resource or template doesn't contain properties + of certain structure (Ex: Only PolicyTemplates are allowed in SAM template) + + ## Plugin Implementation + + ### Defining a plugin + A plugin is a subclass of `BasePlugin` that implements one or more methods capable of processing the life cycle + events. + These methods have a prefix `on_` followed by the name of the life cycle event. For example, to handle + `before_transform_resource` event, implement a method called `on_before_transform_resource`. We call these methods + as "hooks" which are methods capable of handling this event. + + ### Hook Methods + Arguments passed to the hook method is different for each life cycle event. Check out the hook methods in the + `BasePlugin` class for detailed description of the method signature + + ### Raising validation errors + Plugins must raise an `samtranslator.model.exception.InvalidResourceException` when the input SAM template does + not conform to the expectation + set by the plugin. SAM translator will convert this into a nice error message and display to the user. + """ + + def __init__(self, initial_plugins=None): + """ + Initialize the plugins class with an optional list of plugins + + :param BasePlugin or list initial_plugins: Single plugin or a List of plugins to initialize with + """ + self._plugins = [] + + if initial_plugins is None: + initial_plugins = [] + + if not isinstance(initial_plugins, list): + initial_plugins = [initial_plugins] + + for plugin in initial_plugins: + self.register(plugin) + + def register(self, plugin): + """ + Register a plugin. New plugins are added to the end of the plugins list. + + :param samtranslator.plugins.BasePlugin plugin: Instance/subclass of BasePlugin class that implements hooks + :raises ValueError: If plugin is not an instance of samtranslator.plugins.BasePlugin or if it is already + registered + :return: None + """ + + if not plugin or not isinstance(plugin, BasePlugin): + raise ValueError("Plugin must be implemented as a subclass of BasePlugin class") + + if self.is_registered(plugin.name): + raise ValueError("Plugin with name {} is already registered".format(plugin.name)) + + self._plugins.append(plugin) + + def is_registered(self, plugin_name): + """ + Checks if a plugin with given name is already registered + + :param plugin_name: Name of the plugin + :return: True if plugin with given name is already registered. False, otherwise + """ + + return plugin_name in [p.name for p in self._plugins] + + def _get(self, plugin_name): + """ + Retrieves the plugin with given name + + :param plugin_name: Name of the plugin to retrieve + :return samtranslator.plugins.BasePlugin: Returns the plugin object if found. None, otherwise + """ + + for p in self._plugins: + if p.name == plugin_name: + return p + + return None + + def act(self, event, *args, **kwargs): + """ + Act on the specific life cycle event. The action here is to invoke the hook function on all registered plugins. + *args and **kwargs will be passed directly to the plugin's hook functions + + :param samtranslator.plugins.LifeCycleEvents event: Event to act upon + :return: Nothing + :raises ValueError: If event is not a valid life cycle event + :raises NameError: If a plugin does not have the hook method defined + :raises Exception: Any exception that a plugin raises + """ + + if not isinstance(event, LifeCycleEvents): + raise ValueError("'event' must be an instance of LifeCycleEvents class") + + method_name = "on_" + event.name + + for plugin in self._plugins: + + if not hasattr(plugin, method_name): + raise NameError( + "'{}' method is not found in the plugin with name '{}'".format(method_name, plugin.name) + ) + + try: + getattr(plugin, method_name)(*args, **kwargs) + except (InvalidResourceException, InvalidDocumentException, InvalidTemplateException) as ex: + # Don't need to log these because they don't result in crashes + raise ex + except Exception as ex: + LOG.exception("Plugin '%s' raised an exception: %s", plugin.name, ex) + raise ex + + def __len__(self): + """ + Returns the number of plugins registered with this class + + :return integer: Number of plugins registered + """ + return len(self._plugins) diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index 9a83659c0..4c0ec3e34 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -1,5 +1,4 @@ import copy -import json import re from samtranslator.model.intrinsics import ref, make_conditional, fnSub, is_intrinsic_no_value @@ -45,11 +44,11 @@ def __init__(self, doc): modifications on this copy. :param dict doc: Swagger document as a dictionary - :raises ValueError: If the input Swagger document does not meet the basic Swagger requirements. + :raises InvalidDocumentException: If the input Swagger document does not meet the basic Swagger requirements. """ if not SwaggerEditor.is_valid(doc): - raise ValueError("Invalid Swagger document") + raise InvalidDocumentException("Invalid Swagger document") self._doc = copy.deepcopy(doc) self.paths = self._doc["paths"] @@ -187,7 +186,9 @@ def add_lambda_integration( method = self._normalize_method_name(method) if self.has_integration(path, method): - raise ValueError("Lambda integration already exists on Path={}, Method={}".format(path, method)) + raise InvalidDocumentException( + "Lambda integration already exists on Path={}, Method={}".format(path, method) + ) self.add_path(path, method) @@ -251,7 +252,7 @@ def add_state_machine_integration( method = self._normalize_method_name(method) if self.has_integration(path, method): - raise ValueError("Integration already exists on Path={}, Method={}".format(path, method)) + raise InvalidDocumentException("Integration already exists on Path={}, Method={}".format(path, method)) self.add_path(path, method) @@ -388,7 +389,7 @@ def add_cors( :param integer/dict max_age: Maximum duration to cache the CORS Preflight request. Value is set on Access-Control-Max-Age header. Value can also be an intrinsic function dict. :param bool/None allow_credentials: Flags whether request is allowed to contain credentials. - :raises ValueError: When values for one of the allowed_* variables is empty + :raises InvalidTemplateException: When values for one of the allowed_* variables is empty """ for path_item in self.get_conditional_contents(self.paths.get(path)): @@ -1022,13 +1023,13 @@ def _add_iam_resource_policy_for_method(self, policy_list, effect, resource_list """ This method generates a policy statement to grant/deny specific IAM users access to the API method and appends it to the swagger under `x-amazon-apigateway-policy` - :raises ValueError: If the effect passed in does not match the allowed values. + :raises InvalidDocumentException: If the effect passed in does not match the allowed values. """ if not policy_list: return if effect not in ["Allow", "Deny"]: - raise ValueError("Effect must be one of {}".format(["Allow", "Deny"])) + raise InvalidDocumentException("Effect must be one of {}".format(["Allow", "Deny"])) if not isinstance(policy_list, (dict, list)): raise InvalidDocumentException( @@ -1081,7 +1082,7 @@ def _add_ip_resource_policy_for_method(self, ip_list, conditional, resource_list """ This method generates a policy statement to grant/deny specific IP address ranges access to the API method and appends it to the swagger under `x-amazon-apigateway-policy` - :raises ValueError: If the conditional passed in does not match the allowed values. + :raises InvalidDocumentException: If the conditional passed in does not match the allowed values. """ if not ip_list: return @@ -1090,7 +1091,7 @@ def _add_ip_resource_policy_for_method(self, ip_list, conditional, resource_list ip_list = [ip_list] if conditional not in ["IpAddress", "NotIpAddress"]: - raise ValueError("Conditional must be one of {}".format(["IpAddress", "NotIpAddress"])) + raise InvalidDocumentException("Conditional must be one of {}".format(["IpAddress", "NotIpAddress"])) self.resource_policy["Version"] = "2012-10-17" allow_statement = Py27Dict() @@ -1122,11 +1123,11 @@ def _add_vpc_resource_policy_for_method(self, endpoint_dict, conditional, resour """ This method generates a policy statement to grant/deny specific VPC/VPCE access to the API method and appends it to the swagger under `x-amazon-apigateway-policy` - :raises ValueError: If the conditional passed in does not match the allowed values. + :raises InvalidDocumentException: If the conditional passed in does not match the allowed values. """ if conditional not in ["StringNotEquals", "StringEquals"]: - raise ValueError("Conditional must be one of {}".format(["StringNotEquals", "StringEquals"])) + raise InvalidDocumentException("Conditional must be one of {}".format(["StringNotEquals", "StringEquals"])) condition = Py27Dict() string_endpoint_list = endpoint_dict.get("StringEndpointList") diff --git a/samtranslator/translator/translator.py b/samtranslator/translator/translator.py index 5a4fe388f..e4c65f4b0 100644 --- a/samtranslator/translator/translator.py +++ b/samtranslator/translator/translator.py @@ -16,6 +16,7 @@ InvalidResourceException, DuplicateLogicalIdException, InvalidEventException, + InvalidTemplateException, ) from samtranslator.intrinsics.resolver import IntrinsicsResolver from samtranslator.intrinsics.actions import FindInMapAction @@ -23,7 +24,7 @@ from samtranslator.plugins.api.default_definition_body_plugin import DefaultDefinitionBodyPlugin from samtranslator.plugins.application.serverless_app_plugin import ServerlessAppPlugin from samtranslator.plugins import LifeCycleEvents -from samtranslator.plugins import SamPlugins +from samtranslator.plugins.sam_plugins import SamPlugins from samtranslator.plugins.globals.globals_plugin import GlobalsPlugin from samtranslator.plugins.policies.policy_templates_plugin import PolicyTemplatesForResourcePlugin from samtranslator.policy_template_processor.processor import PolicyTemplatesProcessor @@ -61,24 +62,25 @@ def _get_function_names(self, resource_dict, intrinsics_resolver): :return: a dictionary containing api_logical_id as the key and concatenated String of all function_names associated with this api as the value """ - if resource_dict.get("Type") and resource_dict.get("Type").strip() == "AWS::Serverless::Function": - if resource_dict.get("Properties") and resource_dict.get("Properties").get("Events"): - events = list(resource_dict.get("Properties").get("Events").values()) - for item in events: - # If the function event type is `Api` then gets the function name and - # adds to the function_names dict with key as the api_name and value as the function_name - if item.get("Type") == "Api" and item.get("Properties") and item.get("Properties").get("RestApiId"): - rest_api = item.get("Properties").get("RestApiId") - api_name = Api.get_rest_api_id_string(rest_api) - if isinstance(api_name, str): - resource_dict_copy = copy.deepcopy(resource_dict) - function_name = intrinsics_resolver.resolve_parameter_refs( - resource_dict_copy.get("Properties").get("FunctionName") + if resource_dict.get("Type", "").strip() == "AWS::Serverless::Function": + events_properties = resource_dict.get("Properties", {}).get("Events", {}) + events = list(events_properties.values()) if events_properties else [] + for item in events: + # If the function event type is `Api` then gets the function name and + # adds to the function_names dict with key as the api_name and value as the function_name + item_properties = item.get("Properties", {}) + if item.get("Type") == "Api" and item_properties.get("RestApiId"): + rest_api = item_properties.get("RestApiId") + api_name = Api.get_rest_api_id_string(rest_api) + if isinstance(api_name, str): + resource_dict_copy = copy.deepcopy(resource_dict) + function_name = intrinsics_resolver.resolve_parameter_refs( + resource_dict_copy.get("Properties").get("FunctionName") + ) + if function_name: + self.function_names[api_name] = str(self.function_names.get(api_name, "")) + str( + function_name ) - if function_name: - self.function_names[api_name] = str(self.function_names.get(api_name, "")) + str( - function_name - ) return self.function_names def translate(self, sam_template, parameter_values, feature_toggle=None, passthrough_metadata=False): @@ -141,6 +143,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 translated = macro.to_cloudformation(**kwargs) supported_resource_refs = macro.get_resource_references(translated, supported_resource_refs) @@ -162,14 +165,18 @@ def translate(self, sam_template, parameter_values, feature_toggle=None, passthr document_errors.append( DuplicateLogicalIdException(logical_id, resource.logical_id, resource.resource_type) ) - except (InvalidResourceException, InvalidEventException) as e: + except (InvalidResourceException, InvalidEventException, InvalidTemplateException) as e: document_errors.append(e) if deployment_preference_collection.any_enabled(): - template["Resources"].update(deployment_preference_collection.codedeploy_application.to_dict()) + template["Resources"].update(deployment_preference_collection.get_codedeploy_application().to_dict()) + if deployment_preference_collection.needs_resource_condition(): + new_conditions = deployment_preference_collection.create_aggregate_deployment_condition() + if new_conditions: + template.get("Conditions").update(new_conditions) if not deployment_preference_collection.can_skip_service_role(): - template["Resources"].update(deployment_preference_collection.codedeploy_iam_role.to_dict()) + template["Resources"].update(deployment_preference_collection.get_codedeploy_iam_role().to_dict()) for logical_id in deployment_preference_collection.enabled_logical_ids(): try: @@ -182,7 +189,7 @@ def translate(self, sam_template, parameter_values, feature_toggle=None, passthr # Run the after-transform plugin target try: sam_plugins.act(LifeCycleEvents.after_transform_template, template) - except (InvalidDocumentException, InvalidResourceException) as e: + except (InvalidDocumentException, InvalidResourceException, InvalidTemplateException) as e: document_errors.append(e) # Cleanup diff --git a/samtranslator/validator/sam_schema/definitions/api.json b/samtranslator/validator/sam_schema/definitions/api.json index 3558fdb99..10028d888 100644 --- a/samtranslator/validator/sam_schema/definitions/api.json +++ b/samtranslator/validator/sam_schema/definitions/api.json @@ -38,6 +38,13 @@ "Auth": { "$ref": "#definitions/AWS::Serverless::Api.Auth" }, + "ApiKeySourceType": { + "enum": [ + "HEADER", + "AUTHORIZER" + ], + "type":"string" + }, "BinaryMediaTypes": { "items": { "type": [ diff --git a/tests/model/api/test_http_api_generator.py b/tests/model/api/test_http_api_generator.py index 03030b1f7..54f9e287f 100644 --- a/tests/model/api/test_http_api_generator.py +++ b/tests/model/api/test_http_api_generator.py @@ -76,6 +76,103 @@ def test_auth_intrinsic_default_auth(self): with pytest.raises(InvalidResourceException): HttpApiGenerator(**self.kwargs)._construct_http_api() + def test_auth_iam_enabled(self): + self.kwargs["auth"] = { + "EnableIamAuthorizer": True, + } + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + self.assertEqual( + http_api.Body["components"]["securitySchemes"], + { + "AWS_IAM": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4", + } + }, + ) + + def test_enabling_auth_iam_does_not_clobber_conflicting_custom_authorizer(self): + self.kwargs["auth"] = { + "EnableIamAuthorizer": True, + "Authorizers": { + "AWS_IAM": { + "AuthorizationScopes": ["scope"], + "JwtConfiguration": {"config": "value"}, + "IdentitySource": "https://example.com", + } + }, + } + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + self.kwargs["definition_uri"] = None + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + self.assertEqual( + http_api.Body["components"]["securitySchemes"], + { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "jwtConfiguration": {"config": "value"}, + "identitySource": "https://example.com", + "type": "jwt", + }, + } + }, + ) + + def test_auth_iam_enabled_with_default(self): + self.kwargs["auth"] = { + "DefaultAuthorizer": "AWS_IAM", + "EnableIamAuthorizer": True, + } + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + self.assertEqual( + http_api.Body["components"]["securitySchemes"], + { + "AWS_IAM": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4", + } + }, + ) + + def test_auth_missing_iam_enablement(self): + self.kwargs["auth"] = { + "DefaultAuthorizer": "AWS_IAM", + "EnableIamAuthorizer": False, + } + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + with pytest.raises(InvalidResourceException) as e: + HttpApiGenerator(**self.kwargs)._construct_http_api() + self.assertEqual( + e.value.message, + "Resource with id [HttpApiId] is invalid. " + + "Unable to set DefaultAuthorizer because 'AWS_IAM' was not defined in 'Authorizers'.", + ) + + def test_auth_iam_disabled(self): + self.kwargs["auth"] = { + "EnableIamAuthorizer": False, + } + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + self.assertNotIn("components", http_api.Body) + + def test_auth_iam_not_enabled_with_unsupported_values(self): + unsupported_values = [1, "", [], {}, {"Ref": "MyVar"}, "True", None] + for val in unsupported_values: + self.kwargs["auth"] = { + "EnableIamAuthorizer": val, + } + self.kwargs["definition_body"] = OpenApiEditor.gen_skeleton() + http_api = HttpApiGenerator(**self.kwargs)._construct_http_api() + self.assertNotIn("components", http_api.Body, "EnableIamAuthorizer value: %s" % val) + def test_auth_novalue_default_does_not_raise(self): self.kwargs["auth"] = self.authorizers self.kwargs["auth"]["DefaultAuthorizer"] = {"Ref": "AWS::NoValue"} diff --git a/tests/model/eventsources/test_mq_event_source.py b/tests/model/eventsources/test_mq_event_source.py index c6062a7d3..253ac77c4 100644 --- a/tests/model/eventsources/test_mq_event_source.py +++ b/tests/model/eventsources/test_mq_event_source.py @@ -1,5 +1,7 @@ from unittest import TestCase from samtranslator.model.eventsources.pull import MQ +from samtranslator.model.exceptions import InvalidEventException +from parameterized import parameterized class MQEventSource(TestCase): @@ -40,3 +42,57 @@ def test_get_policy_statements(self): } ] self.assertEqual(policy_statements, expected_policy_document) + + @parameterized.expand( + [ + (1,), + (True,), + (["1abc23d4-567f-8ab9-cde0-1fab234c5d67"],), + ({"KmsKeyId": "1abc23d4-567f-8ab9-cde0-1fab234c5d67"},), + ] + ) + def test_must_validate_secrets_manager_kms_key_id(self, kms_key_id_value): + self.mq_event_source.SourceAccessConfigurations = [{"Type": "BASIC_AUTH", "URI": "SECRET_URI"}] + self.mq_event_source.Broker = "BROKER_ARN" + self.mq_event_source.SecretsManagerKmsKeyId = kms_key_id_value + error_message = "(None, 'Provided SecretsManagerKmsKeyId should be of type str.')" + with self.assertRaises(InvalidEventException) as error: + self.mq_event_source.get_policy_statements() + self.assertEqual(error_message, str(error.exception)) + + def test_get_policy_statements_with_secrets_manager_kms_key_id(self): + self.mq_event_source.SourceAccessConfigurations = [{"Type": "BASIC_AUTH", "URI": "SECRET_URI"}] + self.mq_event_source.Broker = "BROKER_ARN" + self.mq_event_source.SecretsManagerKmsKeyId = "1abc23d4-567f-8ab9-cde0-1fab234c5d67" + policy_statements = self.mq_event_source.get_policy_statements() + expected_policy_document = [ + { + "PolicyName": "SamAutoGeneratedAMQPolicy", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue", + ], + "Effect": "Allow", + "Resource": "SECRET_URI", + }, + { + "Action": [ + "mq:DescribeBroker", + ], + "Effect": "Allow", + "Resource": "BROKER_ARN", + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/1abc23d4-567f-8ab9-cde0-1fab234c5d67" + }, + }, + ] + }, + } + ] + self.assertEqual(policy_statements, expected_policy_document) diff --git a/tests/model/eventsources/test_self_managed_kafka_event_source.py b/tests/model/eventsources/test_self_managed_kafka_event_source.py index 1c909d4fc..35567f89c 100644 --- a/tests/model/eventsources/test_self_managed_kafka_event_source.py +++ b/tests/model/eventsources/test_self_managed_kafka_event_source.py @@ -1,6 +1,7 @@ from unittest import TestCase from samtranslator.model.eventsources.pull import SelfManagedKafka from samtranslator.model.exceptions import InvalidEventException +from parameterized import parameterized class SelfManagedKafkaEventSource(TestCase): @@ -294,3 +295,27 @@ def test_must_raise_for_wrong_source_access_configurations_uri(self): self.kafka_event_source.SourceAccessConfigurations = config with self.assertRaises(InvalidEventException): self.kafka_event_source.get_policy_statements() + + @parameterized.expand( + [ + (1,), + (True,), + (["1abc23d4-567f-8ab9-cde0-1fab234c5d67"],), + ({"KmsKeyId": "1abc23d4-567f-8ab9-cde0-1fab234c5d67"},), + ] + ) + def test_must_validate_secrets_manager_kms_key_id(self, kms_key_id_value): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + self.kafka_event_source.SecretsManagerKmsKeyId = kms_key_id_value + error_message = "(None, 'Provided SecretsManagerKmsKeyId should be of type str.')" + with self.assertRaises(InvalidEventException) as error: + self.kafka_event_source.get_policy_statements() + self.assertEqual(error_message, str(error.exception)) diff --git a/tests/model/test_api.py b/tests/model/test_api.py index 708b83a5a..d718d5f8f 100644 --- a/tests/model/test_api.py +++ b/tests/model/test_api.py @@ -3,6 +3,7 @@ from samtranslator.model import InvalidResourceException from samtranslator.model.apigateway import ApiGatewayAuthorizer +from samtranslator.utils.py27hash_fix import Py27Dict class TestApiGatewayAuthorizer(TestCase): @@ -25,6 +26,14 @@ def test_create_authorizer_fails_with_missing_identity_values_and_not_cached(sel function_payload_type="REQUEST", ) + def test_create_authorizer_fails_with_invalid_function_payload_type(self): + with self.assertRaises(InvalidResourceException): + ApiGatewayAuthorizer( + api_logical_id="logicalId", + name="authName", + function_payload_type=Py27Dict({"key": "value"}), + ) + def test_create_authorizer_fails_with_empty_identity(self): with pytest.raises(InvalidResourceException): ApiGatewayAuthorizer( diff --git a/tests/model/test_api_v2.py b/tests/model/test_api_v2.py index 1d5428b8e..17f88d33d 100644 --- a/tests/model/test_api_v2.py +++ b/tests/model/test_api_v2.py @@ -7,6 +7,10 @@ class TestApiGatewayV2Authorizer(TestCase): + def test_create_iam_auth(self): + auth = ApiGatewayV2Authorizer(is_aws_iam_authorizer=True) + self.assertEqual(auth.is_aws_iam_authorizer, True) + def test_create_oauth2_auth(self): auth = ApiGatewayV2Authorizer( api_logical_id="logicalId", diff --git a/tests/openapi/test_openapi.py b/tests/openapi/test_openapi.py index 2c89afc06..6e12e8031 100644 --- a/tests/openapi/test_openapi.py +++ b/tests/openapi/test_openapi.py @@ -20,13 +20,13 @@ def test_must_raise_on_valid_swagger(self): "swagger": "2.0", # "openapi": "2.1.0" "paths": {"/foo": {}, "/bar": {}}, } # missing openapi key word - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): OpenApiEditor(valid_swagger) def test_must_raise_on_invalid_openapi(self): invalid_openapi = {"paths": {}} # Missing "openapi" keyword - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): OpenApiEditor(invalid_openapi) def test_must_succeed_on_valid_openapi(self): @@ -40,13 +40,13 @@ def test_must_succeed_on_valid_openapi(self): def test_must_fail_on_invalid_openapi_version(self): invalid_openapi = {"openapi": "2.3.0", "paths": {"/foo": {}, "/bar": {}}} - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): OpenApiEditor(invalid_openapi) def test_must_fail_on_invalid_openapi_version_2(self): invalid_openapi = {"openapi": "3.1.1.1", "paths": {"/foo": {}, "/bar": {}}} - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): OpenApiEditor(invalid_openapi) def test_must_succeed_on_valid_openapi3(self): diff --git a/tests/plugins/application/test_serverless_app_plugin.py b/tests/plugins/application/test_serverless_app_plugin.py index 5b56846db..cfe67160c 100644 --- a/tests/plugins/application/test_serverless_app_plugin.py +++ b/tests/plugins/application/test_serverless_app_plugin.py @@ -220,10 +220,120 @@ def test_resolve_intrinsics(self): self.assertEqual("value1", output) + @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") + def test_sar_throttling_doesnt_stop_processing(self, SamTemplateMock): + client = Mock() + client.create_cloud_formation_template = Mock() + client.create_cloud_formation_template.side_effect = ClientError( + {"Error": {"Code": "TooManyRequestsException"}}, "CreateCloudFormationTemplate" + ) + + app_resources = [ + ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), + ] + + sam_template = Mock() + SamTemplateMock.return_value = sam_template + sam_template.iterate = Mock() + sam_template.iterate.return_value = app_resources + + self.plugin = ServerlessAppPlugin(sar_client=client) + self.plugin._can_process_application = Mock() + self.plugin._can_process_application.return_value = True + self.plugin._get_sleep_time_sec = Mock() + self.plugin._get_sleep_time_sec.return_value = 0.02 + self.plugin.TEMPLATE_WAIT_TIMEOUT_SECONDS = 1.0 + + self.plugin.on_before_transform_template({}) + self.assertEqual( + self.plugin._applications.get(("id1", "1.0.0")).message, + "Resource with id [id1] is invalid. Failed to call SAR, timeout limit exceeded.", + ) + # confirm we had at least two attempts to call SAR and that we executed a sleep + self.assertGreater(client.create_cloud_formation_template.call_count, 1) + self.assertGreaterEqual(self.plugin._get_sleep_time_sec.call_count, 1) + + @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") + def test_unexpected_sar_error_stops_processing(self, SamTemplateMock): + template_dict = {"a": "b"} + app_resources = [ + ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), + ] + + sam_template = Mock() + SamTemplateMock.return_value = sam_template + sam_template.iterate = Mock() + sam_template.iterate.return_value = app_resources + + client = Mock() + client.create_cloud_formation_template.side_effect = ClientError( + {"Error": {"Code": "BadBadError"}}, "CreateCloudFormationTemplate" + ) + self.plugin = ServerlessAppPlugin(sar_client=client) + self.plugin._can_process_application = Mock() + self.plugin._can_process_application.return_value = True + + with self.assertRaises(ClientError): + self.plugin.on_before_transform_template(template_dict) + + @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") + def test_sar_success_one_app(self, SamTemplateMock): + template_dict = {"a": "b"} + app_resources = [ + ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), + ] + + sam_template = Mock() + SamTemplateMock.return_value = sam_template + sam_template.iterate = Mock() + sam_template.iterate.return_value = app_resources + + client = Mock() + client.create_cloud_formation_template = Mock() + client.create_cloud_formation_template.return_value = {"TemplateUrl": "/URL", "Status": STATUS_ACTIVE} + self.plugin = ServerlessAppPlugin(sar_client=client) + self.plugin._can_process_application = Mock() + self.plugin._can_process_application.return_value = True + self.plugin.on_before_transform_template(template_dict) + + self.assertEqual(client.create_cloud_formation_template.call_count, 1) + + @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") + def test_sleep_between_sar_checks(self, SamTemplateMock): + template_dict = {"a": "b"} + client = Mock() + + app_resources = [ + ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), + ] + + sam_template = Mock() + SamTemplateMock.return_value = sam_template + sam_template.iterate = Mock() + sam_template.iterate.return_value = app_resources + client.create_cloud_formation_template = Mock() + client.create_cloud_formation_template.side_effect = [ + ClientError({"Error": {"Code": "TooManyRequestsException"}}, "CreateCloudFormationTemplate"), + {"TemplateUrl": "/URL", "Status": STATUS_ACTIVE}, + ] + self.plugin._can_process_application = Mock() + self.plugin._can_process_application.return_value = True + self.plugin = ServerlessAppPlugin(sar_client=client, wait_for_template_active_status=True, validate_only=False) + self.plugin._get_sleep_time_sec = Mock() + self.plugin._get_sleep_time_sec.return_value = 0.001 + self.plugin.on_before_transform_template(template_dict) + # should have exactly two calls to SAR + self.assertEqual(client.create_cloud_formation_template.call_count, 2) + self.assertEqual(self.plugin._get_sleep_time_sec.call_count, 1) # make sure we slept once + class ApplicationResource(object): - def __init__(self, app_id="app_id", semver="1.3.5"): - self.properties = {"ApplicationId": app_id, "SemanticVersion": semver} + def __init__(self, app_id="app_id", semver="1.3.5", location=None): + self.properties = ( + {"ApplicationId": app_id, "SemanticVersion": semver} + if not location + else {"Location": {"ApplicationId": app_id, "SemanticVersion": semver}} + ) # class TestServerlessAppPlugin_on_before_transform_resource(TestCase): @@ -328,3 +438,42 @@ def test_sleep_between_sar_checks(self): # should have exactly two calls to SAR self.assertEqual(client.get_cloud_formation_template.call_count, 2) self.assertEqual(plugin._get_sleep_time_sec.call_count, 1) # make sure we slept once + + +class TestServerlessAppPlugin_on_before_and_on_after_transform_template(TestCase): + @patch("samtranslator.plugins.application.serverless_app_plugin.SamTemplate") + def test_time_limit_exceeds_between_combined_sar_calls(self, SamTemplateMock): + template_dict = {"a": "b"} + app_resources = [ + ("id1", ApplicationResource(app_id="id1", semver="1.0.0", location=True)), + ] + + sam_template = Mock() + SamTemplateMock.return_value = sam_template + sam_template.iterate = Mock() + sam_template.iterate.return_value = app_resources + + client = Mock() + client.get_cloud_formation_template = Mock() + client.get_cloud_formation_template.side_effect = [ + ClientError({"Error": {"Code": "TooManyRequestsException"}}, "GetCloudFormationTemplate"), + {"Status": STATUS_ACTIVE}, + ] + client.create_cloud_formation_template = Mock() + client.create_cloud_formation_template.side_effect = [ + ClientError({"Error": {"Code": "TooManyRequestsException"}}, "CreateCloudFormationTemplate"), + {"TemplateUrl": "/URL", "Status": STATUS_ACTIVE}, + ] + plugin = ServerlessAppPlugin(sar_client=client, wait_for_template_active_status=True, validate_only=False) + plugin._get_sleep_time_sec = Mock() + plugin._get_sleep_time_sec.return_value = 0.04 + plugin._in_progress_templates = [("appid", "template"), ("appid2", "template2")] + plugin.TEMPLATE_WAIT_TIMEOUT_SECONDS = 0.08 + + plugin.on_before_transform_template(template_dict) + with self.assertRaises(InvalidResourceException): + plugin.on_after_transform_template(template_dict) + # confirm we had at least two attempts to call SAR and that we executed a sleep + self.assertEqual(client.get_cloud_formation_template.call_count, 1) + self.assertEqual(client.create_cloud_formation_template.call_count, 2) + self.assertGreaterEqual(plugin._get_sleep_time_sec.call_count, 2) diff --git a/tests/swagger/test_swagger.py b/tests/swagger/test_swagger.py index 884329219..84f0f762b 100644 --- a/tests/swagger/test_swagger.py +++ b/tests/swagger/test_swagger.py @@ -18,7 +18,7 @@ class TestSwaggerEditor_init(TestCase): def test_must_raise_on_invalid_swagger(self): invalid_swagger = {"paths": {}} # Missing "Swagger" keyword - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): SwaggerEditor(invalid_swagger) def test_must_succeed_on_valid_swagger(self): @@ -32,13 +32,13 @@ def test_must_succeed_on_valid_swagger(self): def test_must_fail_on_invalid_openapi_version(self): invalid_swagger = {"openapi": "2.3.0", "paths": {"/foo": {}, "/bar": {}}} - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): SwaggerEditor(invalid_swagger) def test_must_fail_on_invalid_openapi_version_2(self): invalid_swagger = {"openapi": "3.1.1.1", "paths": {"/foo": {}, "/bar": {}}} - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): SwaggerEditor(invalid_swagger) def test_must_succeed_on_valid_openapi3(self): @@ -53,7 +53,7 @@ def test_must_succeed_on_valid_openapi3(self): def test_must_fail_with_bad_values_for_path(self, invalid_path_item): invalid_swagger = {"openapi": "3.1.1.1", "paths": {"/foo": {}, "/bad": invalid_path_item}} - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): SwaggerEditor(invalid_swagger) @@ -261,7 +261,7 @@ def test_must_add_new_integration_to_existing_path(self): def test_must_raise_on_existing_integration(self): - with self.assertRaises(ValueError): + with self.assertRaises(InvalidDocumentException): self.editor.add_lambda_integration("/bar", "get", "integrationUri") def test_must_add_credentials_to_the_integration(self): diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 79f7080d9..344ead6c1 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1,5 +1,6 @@ from enum import Enum -from samtranslator.plugins import SamPlugins, BasePlugin, LifeCycleEvents +from samtranslator.plugins import BasePlugin, LifeCycleEvents +from samtranslator.plugins.sam_plugins import SamPlugins from unittest import TestCase from unittest.mock import Mock, patch, call diff --git a/tests/translator/input/api_with_apikey_source.yaml b/tests/translator/input/api_with_apikey_source.yaml new file mode 100644 index 000000000..d2b74e604 --- /dev/null +++ b/tests/translator/input/api_with_apikey_source.yaml @@ -0,0 +1,24 @@ +Resources: + MyApiWithAuthSource: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + ApiKeyRequired: true + ApiKeySourceType: AUTHORIZER + + MyFunctionWithApiKeyRequiredTrue: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs12.x + Events: + MyApiWithApiKeyRequiredTrue: + Type: Api + Properties: + RestApiId: !Ref MyApiWithAuthSource + Path: /ApiKeyTrue + Method: get + Auth: + ApiKeyRequired: true \ No newline at end of file diff --git a/tests/translator/input/error_api_with_models_of_invalid_type.yaml b/tests/translator/input/error_api_with_models_of_invalid_type.yaml new file mode 100644 index 000000000..0a085e0ca --- /dev/null +++ b/tests/translator/input/error_api_with_models_of_invalid_type.yaml @@ -0,0 +1,37 @@ +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Models: + - Invalid_value + + MyFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + InlineCode: | + exports.handler = async (event, context, callback) => { + return { + statusCode: 200, + body: 'Success' + } + } + Events: + None: + Type: Api + Properties: + RequestModel: + Model: User + Required: true + RestApiId: + Ref: MyApi + Method: get + Path: /none + +Outputs: + ApiUrl: + Description: "API endpoint URL for Prod environment" + Value: + Fn::Sub: 'https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/Prod/' \ No newline at end of file diff --git a/tests/translator/input/error_function_api_invalid_properties.yaml b/tests/translator/input/error_function_api_invalid_properties.yaml new file mode 100644 index 000000000..1e50301dc --- /dev/null +++ b/tests/translator/input/error_function_api_invalid_properties.yaml @@ -0,0 +1,21 @@ +Resources: + Function: + Type: AWS::Serverless::Function + Properties: + Runtime: python3.7 + Handler: index.handler + CodeUri: s3://bucket/key + Events: + Api: + Type: HttpApi + Properties: '' + Function2: + Type: AWS::Serverless::Function + Properties: + Runtime: python3.7 + Handler: index.handler + CodeUri: s3://bucket/key + Events: + Api2: + Type: RestApi + Properties: '' diff --git a/tests/translator/input/error_function_with_deployment_preference_passthrough_condition_with_invalid_values.yaml b/tests/translator/input/error_function_with_deployment_preference_passthrough_condition_with_invalid_values.yaml new file mode 100644 index 000000000..6e110e927 --- /dev/null +++ b/tests/translator/input/error_function_with_deployment_preference_passthrough_condition_with_invalid_values.yaml @@ -0,0 +1,72 @@ +# Tests unsupported intrinsic and invalid type in the PassthroughCondition property + +Mappings: + HelloWorldMap: + hello: + key1: true + key2: false + world: + key1: false + key2: true + +Parameters: + FnName: + Type: String + ProvisionedConcurrency: + Type: String + Default: 10 + EnableAliasProvisionedConcurrency: + Type: String + AllowedValues: + - true + - false + Default: true + DefaultTrueParam: + Type: String + Default: "true" + DefaultFalseParam: + Type: String + Default: "false" + HelloWorldParam: + Type: String + Default: "hello" + +Conditions: + AliasProvisionedConcurrencyEnabled: !Equals [!Ref EnableAliasProvisionedConcurrency, true] + FunctionCondition: !Equals [true, true] + +Resources: + InvalidType: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + PassthroughCondition: ["hello"] + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' + + UnsupportedIntrinsic: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + PassthroughCondition: !If + - AliasProvisionedConcurrencyEnabled + - true + - false + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' diff --git a/tests/translator/input/error_function_with_invalid_kms_type_for_self_managed_kafka.yaml b/tests/translator/input/error_function_with_invalid_kms_type_for_self_managed_kafka.yaml new file mode 100644 index 000000000..6157ba957 --- /dev/null +++ b/tests/translator/input/error_function_with_invalid_kms_type_for_self_managed_kafka.yaml @@ -0,0 +1,31 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameter: + SecretsManagerKmsKeyIdValue: + Type: String + Default: 1abc23d4-567f-8ab9-cde0-1fab234c5d67 +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + SourceAccessConfigurations: + - Type: SASL_SCRAM_512_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + - Type: VPC_SECURITY_GROUP + URI: security_group:sg-67890 + SecretsManagerKmsKeyId: + Ref: SecretsManagerKmsKeyIdValue + diff --git a/tests/translator/input/error_function_with_mq_kms_invalid_type.yaml b/tests/translator/input/error_function_with_mq_kms_invalid_type.yaml new file mode 100644 index 000000000..b36af5f42 --- /dev/null +++ b/tests/translator/input/error_function_with_mq_kms_invalid_type.yaml @@ -0,0 +1,24 @@ +Parameter: + SecretsManagerKmsKeyIdValue: + Type: String + Default: 1abc23d4-567f-8ab9-cde0-1fab234c5d67 + +Resources: + MQFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/queues.zip + Handler: queue.mq_handler + Runtime: python2.7 + Events: + MyMQQueue: + Type: MQ + Properties: + Broker: arn:aws:mq:us-east-2:123456789012:broker:MyBroker:b-1234a5b6-78cd-901e-2fgh-3i45j6k178l9 + Queues: + - "Queue1" + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + SecretsManagerKmsKeyId: + Ref: SecretsManagerKmsKeyIdValue \ No newline at end of file diff --git a/tests/translator/input/function_with_deployment_no_service_role_with_passthrough.yaml b/tests/translator/input/function_with_deployment_no_service_role_with_passthrough.yaml new file mode 100644 index 000000000..6a033caee --- /dev/null +++ b/tests/translator/input/function_with_deployment_no_service_role_with_passthrough.yaml @@ -0,0 +1,48 @@ +Conditions: + Condition1: + Fn::Equals: + - true + - true + +Globals: + Function: + AutoPublishAlias: live + DeploymentPreference: + Type: AllAtOnce + Role: !Ref DeploymentRole + PassthroughCondition: true + +Resources: + + MinimalFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + + OtherFunction: + Condition: Condition1 + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + + DeploymentRole: + Type: AWS::IAM::Role + Properties: + ManagedPolicyArns: + - arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Action: + - sts:AssumeRole + Effect: Allow + Principal: + Service: + - codedeploy.amazonaws.com + + + diff --git a/tests/translator/input/function_with_deployment_no_service_role.yaml b/tests/translator/input/function_with_deployment_no_service_role_without_passthrough.yaml similarity index 90% rename from tests/translator/input/function_with_deployment_no_service_role.yaml rename to tests/translator/input/function_with_deployment_no_service_role_without_passthrough.yaml index ae40a6a00..ef92307c8 100644 --- a/tests/translator/input/function_with_deployment_no_service_role.yaml +++ b/tests/translator/input/function_with_deployment_no_service_role_without_passthrough.yaml @@ -1,3 +1,9 @@ +Conditions: + Condition1: + Fn::Equals: + - true + - true + Globals: Function: AutoPublishAlias: live @@ -15,6 +21,7 @@ Resources: Runtime: python2.7 OtherFunction: + Condition: Condition1 Type: 'AWS::Serverless::Function' Properties: CodeUri: s3://sam-demo-bucket/hello.zip diff --git a/tests/translator/input/function_with_deployment_preference_condition_with_passthrough.yaml b/tests/translator/input/function_with_deployment_preference_condition_with_passthrough.yaml new file mode 100644 index 000000000..b112e4d30 --- /dev/null +++ b/tests/translator/input/function_with_deployment_preference_condition_with_passthrough.yaml @@ -0,0 +1,31 @@ +Parameters: + FnName: + Type: String + ProvisionedConcurrency: + Type: String + Default: 10 + EnableAliasProvisionedConcurrency: + Type: String + AllowedValues: + - true + - false + Default: true +Conditions: + AliasProvisionedConcurrencyEnabled: !Equals [!Ref EnableAliasProvisionedConcurrency, true] + FunctionCondition: !Equals [true, true] +Resources: + MinimalFunction: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + PassthroughCondition: true + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' diff --git a/tests/translator/input/function_with_deployment_preference_condition_without_passthrough.yaml b/tests/translator/input/function_with_deployment_preference_condition_without_passthrough.yaml new file mode 100644 index 000000000..06e788bfe --- /dev/null +++ b/tests/translator/input/function_with_deployment_preference_condition_without_passthrough.yaml @@ -0,0 +1,30 @@ +Parameters: + FnName: + Type: String + ProvisionedConcurrency: + Type: String + Default: 10 + EnableAliasProvisionedConcurrency: + Type: String + AllowedValues: + - true + - false + Default: true +Conditions: + AliasProvisionedConcurrencyEnabled: !Equals [!Ref EnableAliasProvisionedConcurrency, true] + FunctionCondition: !Equals [true, true] +Resources: + MinimalFunction: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' \ No newline at end of file diff --git a/tests/translator/input/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.yaml b/tests/translator/input/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.yaml new file mode 100644 index 000000000..78445ec10 --- /dev/null +++ b/tests/translator/input/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.yaml @@ -0,0 +1,91 @@ +Conditions: + Condition1: + Fn::Equals: + - true + - true + Condition2: + Fn::Equals: + - true + - false + Condition3: + Fn::Equals: + - true + - false + +Parameters: + MyFalseParameter: + Type: String + Default: False + +Resources: + MinimalFunction: + Condition: Condition1 + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery2Minutes + PassthroughCondition: true + + MinimalFunctionWithMinimalDeploymentPreference: + Type: 'AWS::Serverless::Function' + Condition: Condition2 + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: livewithdeployment + DeploymentPreference: + Type: Canary10Percent5Minutes + PassthroughCondition: true + + MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms: + Type: 'AWS::Serverless::Function' + Condition: Condition2 + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: livewithdeploymentwithhooksandalarms + DeploymentPreference: + Type: Linear10PercentEvery2Minutes + Hooks: + PreTraffic: !Ref MySanityTestFunction + PostTraffic: !Ref MyValidationTestFunction + Alarms: + - !Ref MyCloudWatchAlarm + PassthroughCondition: true + + MySanityTestFunction: + Type: 'AWS::Serverless::Function' + Condition: Condition3 + Properties: + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://my-bucket/mySanityTestFunction.zip + DeploymentPreference: + Enabled: False + PassthroughCondition: true + + MyValidationTestFunction: + Type: 'AWS::Serverless::Function' + Properties: + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://my-bucket/myValidationTestFunction.zip + DeploymentPreference: + Enabled: !Ref MyFalseParameter + PassthroughCondition: true + + MyCloudWatchAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + ComparisonOperator: GreaterThanThreshold + EvaluationPeriods: 1 + MetricName: MyMetric + Namespace: AWS/EC2 + Period: 300 + Threshold: 10 diff --git a/tests/translator/input/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.yaml b/tests/translator/input/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.yaml new file mode 100644 index 000000000..f9e994109 --- /dev/null +++ b/tests/translator/input/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.yaml @@ -0,0 +1,86 @@ +Conditions: + Condition1: + Fn::Equals: + - true + - true + Condition2: + Fn::Equals: + - true + - false + Condition3: + Fn::Equals: + - true + - false + +Parameters: + MyFalseParameter: + Type: String + Default: False + +Resources: + MinimalFunction: + Condition: Condition1 + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery2Minutes + + MinimalFunctionWithMinimalDeploymentPreference: + Type: 'AWS::Serverless::Function' + Condition: Condition2 + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: livewithdeployment + DeploymentPreference: + Type: Canary10Percent5Minutes + + MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms: + Type: 'AWS::Serverless::Function' + Condition: Condition2 + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: livewithdeploymentwithhooksandalarms + DeploymentPreference: + Type: Linear10PercentEvery2Minutes + Hooks: + PreTraffic: !Ref MySanityTestFunction + PostTraffic: !Ref MyValidationTestFunction + Alarms: + - !Ref MyCloudWatchAlarm + + MySanityTestFunction: + Type: 'AWS::Serverless::Function' + Condition: Condition3 + Properties: + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://my-bucket/mySanityTestFunction.zip + DeploymentPreference: + Enabled: False + + MyValidationTestFunction: + Type: 'AWS::Serverless::Function' + Properties: + Handler: hello.handler + Runtime: python2.7 + CodeUri: s3://my-bucket/myValidationTestFunction.zip + DeploymentPreference: + Enabled: !Ref MyFalseParameter + + MyCloudWatchAlarm: + Type: AWS::CloudWatch::Alarm + Properties: + ComparisonOperator: GreaterThanThreshold + EvaluationPeriods: 1 + MetricName: MyMetric + Namespace: AWS/EC2 + Period: 300 + Threshold: 10 diff --git a/tests/translator/input/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.yaml b/tests/translator/input/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.yaml new file mode 100644 index 000000000..0c438d554 --- /dev/null +++ b/tests/translator/input/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.yaml @@ -0,0 +1,110 @@ +# Tests supported intrinsics in the PassthroughCondition property + +Mappings: + HelloWorldMap: + hello: + key1: true + key2: false + world: + key1: false + key2: true + +Parameters: + FnName: + Type: String + ProvisionedConcurrency: + Type: String + Default: 10 + EnableAliasProvisionedConcurrency: + Type: String + AllowedValues: + - true + - false + Default: true + DefaultTrueParam: + Type: String + Default: "true" + DefaultFalseParam: + Type: String + Default: "false" + HelloParam: + Type: String + Default: "hello" + WorldParam: + Type: String + Default: "world" + +Conditions: + AliasProvisionedConcurrencyEnabled: !Equals [!Ref EnableAliasProvisionedConcurrency, true] + FunctionCondition: !Equals [true, true] + +Resources: + TrueRef: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + PassthroughCondition: !Ref DefaultTrueParam + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' + + FalseRef: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + PassthroughCondition: !Ref DefaultFalseParam + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' + + TrueFindInMap: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + PassthroughCondition: !FindInMap + - HelloWorldMap + - !Ref HelloParam + - key1 + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' + + FalseFindInMap: + Type: 'AWS::Serverless::Function' + Condition: FunctionCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 + AutoPublishAlias: live + DeploymentPreference: + Type: Linear10PercentEvery3Minutes + PassthroughCondition: !FindInMap + - HelloWorldMap + - !Ref WorldParam + - key1 + ProvisionedConcurrencyConfig: !If + - AliasProvisionedConcurrencyEnabled + - ProvisionedConcurrentExecutions: !Ref ProvisionedConcurrency + - !Ref 'AWS::NoValue' \ No newline at end of file diff --git a/tests/translator/input/function_with_function_url_config_conditions.yaml b/tests/translator/input/function_with_function_url_config_conditions.yaml new file mode 100644 index 000000000..40c2bce95 --- /dev/null +++ b/tests/translator/input/function_with_function_url_config_conditions.yaml @@ -0,0 +1,35 @@ +AWSTemplateFormatVersion: '2010-09-09' +Conditions: + MyCondition: + Fn::Equals: + - true + - true +Parameters: {} +Resources: + MyFunction: + Condition: MyCondition + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Description: Created by SAM + Handler: index.handler + MemorySize: 1024 + Runtime: nodejs12.x + Timeout: 3 + FunctionUrlConfig: + AuthType: NONE + Cors: + AllowOrigins: + - "https://example.com" + - "example1.com" + - "example2.com" + - "example2.com" + AllowMethods: + - "GET" + AllowCredentials: true + AllowHeaders: + - "x-Custom-Header" + ExposeHeaders: + - "x-amzn-header" + MaxAge: 10 + diff --git a/tests/translator/input/http_api_custom_iam_auth.yaml b/tests/translator/input/http_api_custom_iam_auth.yaml new file mode 100644 index 000000000..35220d023 --- /dev/null +++ b/tests/translator/input/http_api_custom_iam_auth.yaml @@ -0,0 +1,44 @@ +# This test-case tests what happens when an AWS_IAM authorizer is defined on an HttpApi but not enabled anywhere else. +# While the defined authorizer isn't really a true Iam authorizer (it's just a poorly-named OAuth authorizer) this shouldn't cause an error. +Resources: + HttpApiFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/todo_list.zip + Handler: index.restapi + Runtime: python3.7 + Events: + HelloAWSIAMAuth: + Type: HttpApi + Properties: + Path: /hello-aws-iam-auth + Method: get + ApiId: !Ref MyApi + Auth: + Authorizer: AWS_IAM + + MyAuthFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs12.x + + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + Tags: + Tag1: value1 + Tag2: value2 + Auth: + Authorizers: + AWS_IAM: + AuthorizationScopes: + - scope + IdentitySource: $request.header.Authorization + JwtConfiguration: + audience: + - audience1 + - audience2 + issuer: "https://www.example.com/v1/connect/oidc" + DefaultAuthorizer: AWS_IAM diff --git a/tests/translator/input/http_api_global_iam_auth_enabled.yaml b/tests/translator/input/http_api_global_iam_auth_enabled.yaml new file mode 100644 index 000000000..84092f5ee --- /dev/null +++ b/tests/translator/input/http_api_global_iam_auth_enabled.yaml @@ -0,0 +1,28 @@ +Globals: + HttpApi: + Auth: + EnableIamAuthorizer: true +Resources: + HttpApiFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/todo_list.zip + Handler: index.restapi + Runtime: python3.7 + Events: + # The following events use the implicit AWS::Serverless::HttpApi called "ServerlessHttpApi". + # The Iam Authorizer of the implicit AWS::Serverless::HttpApi is enabled using the global above. + # Should not have any auth enabled because there is no one set as the default on the implicit HttpApi. + ImplicitApiDefaultAuthEvent: + Type: HttpApi + Properties: + Path: /default-auth + Method: GET + # Should have Iam auth as it is set here and enabled globally. + ImplicitApiIamAuthEvent: + Type: HttpApi + Properties: + Auth: + Authorizer: AWS_IAM + Path: /iam-auth + Method: GET diff --git a/tests/translator/input/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.yaml b/tests/translator/input/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.yaml new file mode 100644 index 000000000..c0c05c7b4 --- /dev/null +++ b/tests/translator/input/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.yaml @@ -0,0 +1,23 @@ +# This test-case tests what happens when an AWS_IAM authorizer is defined on an HttpApi and also enabled globally. +# In this case the defined authorizer should NOT be overwritten. +Globals: + HttpApi: + Auth: + EnableIamAuthorizer: true +Resources: + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + Auth: + Authorizers: + AWS_IAM: + AuthorizationScopes: + - scope + IdentitySource: $request.header.Authorization + JwtConfiguration: + audience: + - audience1 + - audience2 + issuer: "https://www.example.com/v1/connect/oidc" + DefaultAuthorizer: AWS_IAM + # EnableIamAuthorizer: true diff --git a/tests/translator/input/http_api_local_iam_auth_enabled.yaml b/tests/translator/input/http_api_local_iam_auth_enabled.yaml new file mode 100644 index 000000000..50c7c38fe --- /dev/null +++ b/tests/translator/input/http_api_local_iam_auth_enabled.yaml @@ -0,0 +1,77 @@ +Globals: + HttpApi: + Auth: + EnableIamAuthorizer: false +Resources: + HttpApiFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/todo_list.zip + Handler: index.restapi + Runtime: python3.7 + Events: + # The following events use the defined AWS::Serverless::HttpApi, MyDefaultIamAuthHttpApi, further down. + # This HttpApi has Iam auth enabled AND set as the default. + # Should not have any auth enabled as the authorizer is specifically set to "NONE". + MyDefaultIamAuthHttpApiNoAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyDefaultIamAuthHttpApi + Auth: + Authorizer: NONE + Path: /no-auth + Method: GET + # Should have Iam auth as it is set as the default for the Api. + MyDefaultIamAuthHttpApiDefaultAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyDefaultIamAuthHttpApi + Path: /default-auth + Method: GET + # Should have Iam auth as it is set here. + MyDefaultIamAuthHttpApiIamAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyDefaultIamAuthHttpApi + Auth: + Authorizer: AWS_IAM + Path: /iam-auth + Method: GET + # The following events use the defined AWS::Serverless::HttpApi, MyIamAuthEnabledHttpApi, further down. + # This HttpApi has Iam auth enabled but NOT set as the default. + # Should not have any auth enabled because there is no one set as the default. + MyIamAuthEnabledHttpApiDefaultAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyIamAuthEnabledHttpApi + Path: /default-auth + Method: GET + # Should have Iam auth as it is set here. + MyIamAuthEnabledHttpApiIamAuthEvent: + Type: HttpApi + Properties: + ApiId: + Ref: MyIamAuthEnabledHttpApi + Auth: + Authorizer: AWS_IAM + Path: /iam-auth + Method: GET + + # HTTP API resource with the Iam authorizer enabled and set to the default. + MyDefaultIamAuthHttpApi: + Type: AWS::Serverless::HttpApi + Properties: + Auth: + EnableIamAuthorizer: true + DefaultAuthorizer: AWS_IAM + + # HTTP API resource with the Iam authorizer enabled and NOT set to the default. + MyIamAuthEnabledHttpApi: + Type: AWS::Serverless::HttpApi + Properties: + Auth: + EnableIamAuthorizer: true \ No newline at end of file diff --git a/tests/translator/input/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.yaml b/tests/translator/input/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.yaml new file mode 100644 index 000000000..38430e67c --- /dev/null +++ b/tests/translator/input/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.yaml @@ -0,0 +1,19 @@ +# This test-case tests what happens when an AWS_IAM authorizer is defined on an HttpApi and also enabled locally. +# In this case the defined authorizer should NOT be overwritten. +Resources: + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + Auth: + Authorizers: + AWS_IAM: + AuthorizationScopes: + - scope + IdentitySource: $request.header.Authorization + JwtConfiguration: + audience: + - audience1 + - audience2 + issuer: "https://www.example.com/v1/connect/oidc" + DefaultAuthorizer: AWS_IAM + EnableIamAuthorizer: true diff --git a/tests/translator/input/http_api_multiple_authorizers.yaml b/tests/translator/input/http_api_multiple_authorizers.yaml index d23a443ce..cb5633c83 100644 --- a/tests/translator/input/http_api_multiple_authorizers.yaml +++ b/tests/translator/input/http_api_multiple_authorizers.yaml @@ -30,6 +30,14 @@ Resources: Type: HttpApi Properties: ApiId: !Ref MyApi + HelloAWSIAMAuth: + Type: HttpApi + Properties: + Path: /hello-aws-iam-auth + Method: get + ApiId: !Ref MyApi + Auth: + Authorizer: AWS_IAM MyAuthFn: Type: AWS::Serverless::Function @@ -58,4 +66,5 @@ Resources: - audience1 - audience2 issuer: "https://www.example.com/v1/connect/oidc" - DefaultAuthorizer: LambdaAuth \ No newline at end of file + DefaultAuthorizer: LambdaAuth + EnableIamAuthorizer: true diff --git a/tests/translator/model/preferences/test_deployment_preference.py b/tests/translator/model/preferences/test_deployment_preference.py index e6fbc9d8e..2f7a7645d 100644 --- a/tests/translator/model/preferences/test_deployment_preference.py +++ b/tests/translator/model/preferences/test_deployment_preference.py @@ -16,31 +16,51 @@ def setUp(self): "TriggerTargetArn": {"Ref": "MySNSTopic"}, "TriggerName": "TestTrigger", } - self.expected_deployment_preference = DeploymentPreference( - self.deployment_type, + self.condition = "condition" + + def test_from_dict_with_intrinsic_function_type(self): + + type = {"Ref": "SomeType"} + expected_deployment_preference = DeploymentPreference( + type, self.pre_traffic_hook, self.post_traffic_hook, self.alarms, True, self.role, self.trigger_configurations, + None, ) - def test_from_dict_with_intrinsic_function_type(self): + deployment_preference_yaml_dict = dict() + deployment_preference_yaml_dict["Type"] = type + deployment_preference_yaml_dict["Hooks"] = { + "PreTraffic": self.pre_traffic_hook, + "PostTraffic": self.post_traffic_hook, + } + deployment_preference_yaml_dict["Alarms"] = self.alarms + deployment_preference_yaml_dict["Role"] = self.role + deployment_preference_yaml_dict["TriggerConfigurations"] = self.trigger_configurations + deployment_preference_from_yaml_dict = DeploymentPreference.from_dict( + "logical_id", deployment_preference_yaml_dict, self.condition + ) - type = {"Ref": "SomeType"} + self.assertEqual(expected_deployment_preference, deployment_preference_from_yaml_dict) + + def test_from_dict(self): expected_deployment_preference = DeploymentPreference( - type, + self.deployment_type, self.pre_traffic_hook, self.post_traffic_hook, self.alarms, True, self.role, self.trigger_configurations, + None, ) deployment_preference_yaml_dict = dict() - deployment_preference_yaml_dict["Type"] = type + deployment_preference_yaml_dict["Type"] = self.deployment_type deployment_preference_yaml_dict["Hooks"] = { "PreTraffic": self.pre_traffic_hook, "PostTraffic": self.post_traffic_hook, @@ -49,12 +69,23 @@ def test_from_dict_with_intrinsic_function_type(self): deployment_preference_yaml_dict["Role"] = self.role deployment_preference_yaml_dict["TriggerConfigurations"] = self.trigger_configurations deployment_preference_from_yaml_dict = DeploymentPreference.from_dict( - "logical_id", deployment_preference_yaml_dict + "logical_id", deployment_preference_yaml_dict, self.condition ) self.assertEqual(expected_deployment_preference, deployment_preference_from_yaml_dict) - def test_from_dict(self): + def test_from_dict_with_passthrough_condition(self): + expected_deployment_preference = DeploymentPreference( + self.deployment_type, + self.pre_traffic_hook, + self.post_traffic_hook, + self.alarms, + True, + self.role, + self.trigger_configurations, + self.condition, + ) + deployment_preference_yaml_dict = dict() deployment_preference_yaml_dict["Type"] = self.deployment_type deployment_preference_yaml_dict["Hooks"] = { @@ -64,14 +95,15 @@ def test_from_dict(self): deployment_preference_yaml_dict["Alarms"] = self.alarms deployment_preference_yaml_dict["Role"] = self.role deployment_preference_yaml_dict["TriggerConfigurations"] = self.trigger_configurations + deployment_preference_yaml_dict["PassthroughCondition"] = True deployment_preference_from_yaml_dict = DeploymentPreference.from_dict( - "logical_id", deployment_preference_yaml_dict + "logical_id", deployment_preference_yaml_dict, self.condition ) - self.assertEqual(self.expected_deployment_preference, deployment_preference_from_yaml_dict) + self.assertEqual(expected_deployment_preference, deployment_preference_from_yaml_dict) def test_from_dict_with_disabled_preference_does_not_require_other_parameters(self): - expected_deployment_preference = DeploymentPreference(None, None, None, None, False, None, None) + expected_deployment_preference = DeploymentPreference(None, None, None, None, False, None, None, None) deployment_preference_yaml_dict = dict() deployment_preference_yaml_dict["Enabled"] = False @@ -82,7 +114,7 @@ def test_from_dict_with_disabled_preference_does_not_require_other_parameters(se self.assertEqual(expected_deployment_preference, deployment_preference_from_yaml_dict) def test_from_dict_with_string_disabled_preference_does_not_require_other_parameters(self): - expected_deployment_preference = DeploymentPreference(None, None, None, None, False, None, None) + expected_deployment_preference = DeploymentPreference(None, None, None, None, False, None, None, None) deployment_preference_yaml_dict = dict() deployment_preference_yaml_dict["Enabled"] = "False" @@ -93,7 +125,7 @@ def test_from_dict_with_string_disabled_preference_does_not_require_other_parame self.assertEqual(expected_deployment_preference, deployment_preference_from_yaml_dict) def test_from_dict_with_lowercase_string_disabled_preference_does_not_require_other_parameters(self): - expected_deployment_preference = DeploymentPreference(None, None, None, None, False, None, None) + expected_deployment_preference = DeploymentPreference(None, None, None, None, False, None, None, None) deployment_preference_yaml_dict = dict() deployment_preference_yaml_dict["Enabled"] = "false" diff --git a/tests/translator/model/preferences/test_deployment_preference_collection.py b/tests/translator/model/preferences/test_deployment_preference_collection.py index 6ca0d2c59..2ee6bf81d 100644 --- a/tests/translator/model/preferences/test_deployment_preference_collection.py +++ b/tests/translator/model/preferences/test_deployment_preference_collection.py @@ -18,6 +18,7 @@ def setup_method(self, method): self.post_traffic_host_global = "post_traffic_function_ref" self.pre_traffic_hook_global = "pre_traffic_function_ref" self.function_logical_id = "FunctionLogicalId" + self.condition = "CodeDeployCondition" @patch("boto3.session.Session.region_name", "ap-southeast-1") def test_when_no_global_dict_each_local_deployment_preference_requires_parameters(self): @@ -37,7 +38,7 @@ def test_codedeploy_application(self): expected_codedeploy_application_resource.ComputePlatform = "Lambda" self.assertEqual( - DeploymentPreferenceCollection().codedeploy_application.to_dict(), + DeploymentPreferenceCollection().get_codedeploy_application().to_dict(), expected_codedeploy_application_resource.to_dict(), ) @@ -59,7 +60,7 @@ def test_codedeploy_iam_role(self): ] self.assertEqual( - DeploymentPreferenceCollection().codedeploy_iam_role.to_dict(), expected_codedeploy_iam_role.to_dict() + DeploymentPreferenceCollection().get_codedeploy_iam_role().to_dict(), expected_codedeploy_iam_role.to_dict() ) @patch("boto3.session.Session.region_name", "ap-southeast-1") diff --git a/tests/translator/output/api_with_apikey_source.json b/tests/translator/output/api_with_apikey_source.json new file mode 100644 index 000000000..1e5bd9e57 --- /dev/null +++ b/tests/translator/output/api_with_apikey_source.json @@ -0,0 +1,141 @@ +{ + "Resources": { + "MyApiWithAuthSourceProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithAuthSourceDeployment3febcdd011" + }, + "RestApiId": { + "Ref": "MyApiWithAuthSource" + }, + "StageName": "Prod" + } + }, + "MyFunctionWithApiKeyRequiredTrueMyApiWithApiKeyRequiredTruePermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunctionWithApiKeyRequiredTrue" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/ApiKeyTrue", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithAuthSource" + } + } + ] + } + } + }, + "MyFunctionWithApiKeyRequiredTrue": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Role": { + "Fn::GetAtt": [ + "MyFunctionWithApiKeyRequiredTrueRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MyApiWithAuthSourceDeployment3febcdd011": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 3febcdd011fdd1feeb91bc6a1e04bd43987accd3", + "RestApiId": { + "Ref": "MyApiWithAuthSource" + }, + "StageName": "Stage" + } + }, + "MyFunctionWithApiKeyRequiredTrueRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyApiWithAuthSource": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "ApiKeySourceType": "AUTHORIZER", + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/ApiKeyTrue": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionWithApiKeyRequiredTrue.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_apikey_source.json b/tests/translator/output/aws-cn/api_with_apikey_source.json new file mode 100644 index 000000000..82ba813b1 --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_apikey_source.json @@ -0,0 +1,149 @@ +{ + "Resources": { + "MyFunctionWithApiKeyRequiredTrueMyApiWithApiKeyRequiredTruePermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithApiKeyRequiredTrue" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/ApiKeyTrue", + { + "__ApiId__": { + "Ref": "MyApiWithAuthSource" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiWithAuthSource": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/ApiKeyTrue": { + "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/${MyFunctionWithApiKeyRequiredTrue.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "ApiKeySourceType": "AUTHORIZER" + } + }, + "MyApiWithAuthSourceDeployment3ffbb73485": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 3ffbb734857a2cb9c3133fc57da60c4ea2a162c1", + "RestApiId": { + "Ref": "MyApiWithAuthSource" + }, + "StageName": "Stage" + } + }, + "MyFunctionWithApiKeyRequiredTrue": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionWithApiKeyRequiredTrueRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyApiWithAuthSourceProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithAuthSourceDeployment3ffbb73485" + }, + "RestApiId": { + "Ref": "MyApiWithAuthSource" + }, + "StageName": "Prod" + } + }, + "MyFunctionWithApiKeyRequiredTrueRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_deployment_no_service_role_with_passthrough.json b/tests/translator/output/aws-cn/function_with_deployment_no_service_role_with_passthrough.json new file mode 100644 index 000000000..801fd6cb3 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_deployment_no_service_role_with_passthrough.json @@ -0,0 +1,283 @@ +{ + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Resources": { + "OtherFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Ref": "DeploymentRole" + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "AllAtOnce" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition1" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MinimalFunctionRole": { + "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" + } + ] + } + }, + "OtherFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "OtherFunction" + } + }, + "Condition": "Condition1" + }, + "OtherFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition1" + }, + "DeploymentRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + } + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Ref": "DeploymentRole" + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "AllAtOnce" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + } + }, + "OtherFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "OtherFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + }, + "Name": "live" + } + }, + "OtherFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "OtherFunctionDeploymentGroup" + } + } + }, + "Condition": "Condition1", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "OtherFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "OtherFunction" + }, + "Name": "live" + } + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_deployment_no_service_role.json b/tests/translator/output/aws-cn/function_with_deployment_no_service_role_without_passthrough.json similarity index 67% rename from tests/translator/output/aws-cn/function_with_deployment_no_service_role.json rename to tests/translator/output/aws-cn/function_with_deployment_no_service_role_without_passthrough.json index 65d6a4483..704201e99 100644 --- a/tests/translator/output/aws-cn/function_with_deployment_no_service_role.json +++ b/tests/translator/output/aws-cn/function_with_deployment_no_service_role_without_passthrough.json @@ -1,79 +1,78 @@ { + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, "Resources": { "OtherFunctionDeploymentGroup": { - "Type": "AWS::CodeDeploy::DeploymentGroup", + "Type": "AWS::CodeDeploy::DeploymentGroup", "Properties": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "AutoRollbackConfiguration": { - "Enabled": true, + "Enabled": true, "Events": [ - "DEPLOYMENT_FAILURE", - "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST" ] - }, + }, "ServiceRoleArn": { "Ref": "DeploymentRole" - }, + }, "DeploymentConfigName": { "Fn::Sub": [ - "CodeDeployDefault.Lambda${ConfigName}", + "CodeDeployDefault.Lambda${ConfigName}", { "ConfigName": "AllAtOnce" } ] - }, + }, "DeploymentStyle": { - "DeploymentType": "BLUE_GREEN", + "DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL" } } - }, + }, "MinimalFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { + "Handler": "hello.handler", "Code": { - "S3Bucket": "sam-demo-bucket", + "S3Bucket": "sam-demo-bucket", "S3Key": "hello.zip" - }, - "Handler": "hello.handler", + }, "Role": { "Fn::GetAtt": [ - "MinimalFunctionRole", + "MinimalFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", + }, + "Runtime": "python2.7", "Tags": [ { - "Value": "SAM", + "Value": "SAM", "Key": "lambda:createdBy" } ] } - }, + }, "MinimalFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" @@ -81,21 +80,7 @@ } } ] - } - } - }, - "OtherFunctionVersion640128d35d": { - "DeletionPolicy": "Retain", - "Type": "AWS::Lambda::Version", - "Properties": { - "FunctionName": { - "Ref": "OtherFunction" - } - } - }, - "OtherFunctionRole": { - "Type": "AWS::IAM::Role", - "Properties": { + }, "ManagedPolicyArns": [ "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ], @@ -104,15 +89,30 @@ "Value": "SAM", "Key": "lambda:createdBy" } - ], + ] + } + }, + "OtherFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "OtherFunction" + } + }, + "Condition": "Condition1" + }, + "OtherFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" @@ -120,23 +120,33 @@ } } ] - } - } - }, + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, "DeploymentRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "codedeploy.amazonaws.com" @@ -146,125 +156,127 @@ ] } } - }, + }, "MinimalFunctionDeploymentGroup": { - "Type": "AWS::CodeDeploy::DeploymentGroup", + "Type": "AWS::CodeDeploy::DeploymentGroup", "Properties": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "AutoRollbackConfiguration": { - "Enabled": true, + "Enabled": true, "Events": [ - "DEPLOYMENT_FAILURE", - "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST" ] - }, + }, "ServiceRoleArn": { "Ref": "DeploymentRole" - }, + }, "DeploymentConfigName": { "Fn::Sub": [ - "CodeDeployDefault.Lambda${ConfigName}", + "CodeDeployDefault.Lambda${ConfigName}", { "ConfigName": "AllAtOnce" } ] - }, + }, "DeploymentStyle": { - "DeploymentType": "BLUE_GREEN", + "DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL" } } - }, + }, "ServerlessDeploymentApplication": { - "Type": "AWS::CodeDeploy::Application", + "Type": "AWS::CodeDeploy::Application", "Properties": { "ComputePlatform": "Lambda" } - }, + }, "MinimalFunctionVersion640128d35d": { - "DeletionPolicy": "Retain", - "Type": "AWS::Lambda::Version", + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { "Ref": "MinimalFunction" } } - }, + }, "OtherFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { + "Handler": "hello.handler", "Code": { - "S3Bucket": "sam-demo-bucket", + "S3Bucket": "sam-demo-bucket", "S3Key": "hello.zip" - }, - "Handler": "hello.handler", + }, "Role": { "Fn::GetAtt": [ - "OtherFunctionRole", + "OtherFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", + }, + "Runtime": "python2.7", "Tags": [ { - "Value": "SAM", + "Value": "SAM", "Key": "lambda:createdBy" } ] - } - }, + }, + "Condition": "Condition1" + }, "MinimalFunctionAliaslive": { - "Type": "AWS::Lambda::Alias", + "Type": "AWS::Lambda::Alias", "UpdatePolicy": { "CodeDeployLambdaAliasUpdate": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "DeploymentGroupName": { "Ref": "MinimalFunctionDeploymentGroup" } } - }, + }, "Properties": { "FunctionVersion": { "Fn::GetAtt": [ - "MinimalFunctionVersion640128d35d", + "MinimalFunctionVersion640128d35d", "Version" ] - }, + }, "FunctionName": { "Ref": "MinimalFunction" - }, + }, "Name": "live" } - }, + }, "OtherFunctionAliaslive": { - "Type": "AWS::Lambda::Alias", + "Type": "AWS::Lambda::Alias", "UpdatePolicy": { "CodeDeployLambdaAliasUpdate": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "DeploymentGroupName": { "Ref": "OtherFunctionDeploymentGroup" } } - }, + }, + "Condition": "Condition1", "Properties": { "FunctionVersion": { "Fn::GetAtt": [ - "OtherFunctionVersion640128d35d", + "OtherFunctionVersion640128d35d", "Version" ] - }, + }, "FunctionName": { "Ref": "OtherFunction" - }, + }, "Name": "live" } } } -} \ No newline at end of file +} diff --git a/tests/translator/output/aws-cn/function_with_deployment_preference_condition_with_passthrough.json b/tests/translator/output/aws-cn/function_with_deployment_preference_condition_with_passthrough.json new file mode 100644 index 000000000..1e82b6612 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_deployment_preference_condition_with_passthrough.json @@ -0,0 +1,208 @@ +{ + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "EnableAliasProvisionedConcurrency": { + "Default": true, + "Type": "String", + "AllowedValues": [ + true, + false + ] + }, + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Default": 10, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "FunctionCondition" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "FunctionCondition" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "FunctionCondition" + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "FunctionCondition", + "Properties": { + "Name": "live", + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + } + } + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_deployment_preference_condition_without_passthrough.json b/tests/translator/output/aws-cn/function_with_deployment_preference_condition_without_passthrough.json new file mode 100644 index 000000000..5660e1f9f --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_deployment_preference_condition_without_passthrough.json @@ -0,0 +1,205 @@ +{ + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "EnableAliasProvisionedConcurrency": { + "Default": true, + "Type": "String", + "AllowedValues": [ + true, + false + ] + }, + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Default": 10, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + } + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "FunctionCondition" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "FunctionCondition" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "FunctionCondition", + "Properties": { + "Name": "live", + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + } + } + } + } + } \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json b/tests/translator/output/aws-cn/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json new file mode 100644 index 000000000..2999c3fd1 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json @@ -0,0 +1,585 @@ +{ + "Conditions": { + "ServerlessCodeDeployCondition": { + "Fn::Or": [ + { + "Condition": "Condition2" + }, + { + "Condition": "Condition1" + } + ] + }, + "Condition3": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition2": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "MyFalseParameter": { + "Default": false, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, + "MyCloudWatchAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "EvaluationPeriods": 1, + "Namespace": "AWS/EC2", + "Period": 300, + "ComparisonOperator": "GreaterThanThreshold", + "Threshold": 10, + "MetricName": "MyMetric" + } + }, + "MinimalFunctionWithMinimalDeploymentPreference": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition1" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + } + }, + "Condition": "Condition2" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "ServerlessCodeDeployCondition" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Canary10Percent5Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsAliaslivewithdeploymentwithhooksandalarms": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "BeforeAllowTrafficHook": { + "Ref": "MySanityTestFunction" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup" + }, + "AfterAllowTrafficHook": { + "Ref": "MyValidationTestFunction" + } + } + }, + "Condition": "Condition2", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + }, + "Name": "livewithdeploymentwithhooksandalarms" + } + }, + "MyValidationTestFunctionRole": { + "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" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceRole": { + "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" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition1" + }, + "MySanityTestFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition3" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "AlarmConfiguration": { + "Alarms": [ + { + "Name": { + "Ref": "MyCloudWatchAlarm" + } + } + ], + "Enabled": true + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole": { + "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" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceAliaslivewithdeployment": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup" + } + } + }, + "Condition": "Condition2", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + }, + "Name": "livewithdeployment" + } + }, + "MySanityTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "mySanityTestFunction.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MySanityTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition3" + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + }, + "Condition": "ServerlessCodeDeployCondition" + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "Condition1" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "Condition1", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + }, + "Name": "live" + } + }, + "MyValidationTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "myValidationTestFunction.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MyValidationTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json b/tests/translator/output/aws-cn/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json new file mode 100644 index 000000000..6e2b17324 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json @@ -0,0 +1,570 @@ +{ + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + }, + "Condition2": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition3": { + "Fn::Equals": [ + true, + false + ] + } + }, + "Parameters": { + "MyFalseParameter": { + "Type": "String", + "Default": false + } + }, + "Resources": { + "MyCloudWatchAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "MetricName": "MyMetric", + "Namespace": "AWS/EC2", + "Period": 300, + "Threshold": 10 + } + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition1", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition1", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + } + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition1", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "MinimalFunction" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition1", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreference": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition2", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition2", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceAliaslivewithdeployment": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition2", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup" + } + } + }, + "Properties": { + "Name": "livewithdeployment", + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition2", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition2", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition2", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsAliaslivewithdeploymentwithhooksandalarms": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition2", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup" + }, + "BeforeAllowTrafficHook": { + "Ref": "MySanityTestFunction" + }, + "AfterAllowTrafficHook": { + "Ref": "MyValidationTestFunction" + } + } + }, + "Properties": { + "Name": "livewithdeploymentwithhooksandalarms", + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition2", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MySanityTestFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition3", + "Properties": { + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "mySanityTestFunction.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MySanityTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MySanityTestFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition3", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyValidationTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "myValidationTestFunction.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MyValidationTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyValidationTestFunctionRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ] + } + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Canary10Percent5Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "AlarmConfiguration": { + "Enabled": true, + "Alarms": [ + { + "Name": { + "Ref": "MyCloudWatchAlarm" + } + } + ] + }, + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json b/tests/translator/output/aws-cn/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json new file mode 100644 index 000000000..7e26228d8 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json @@ -0,0 +1,649 @@ +{ + "Mappings": { + "HelloWorldMap": { + "hello": { + "key1": true, + "key2": false + }, + "world": { + "key1": false, + "key2": true + } + } + }, + "Parameters": { + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Type": "String", + "Default": 10 + }, + "EnableAliasProvisionedConcurrency": { + "Type": "String", + "AllowedValues": [ + true, + false + ], + "Default": true + }, + "DefaultTrueParam": { + "Type": "String", + "Default": "true" + }, + "DefaultFalseParam": { + "Type": "String", + "Default": "false" + }, + "HelloParam": { + "Type": "String", + "Default": "hello" + }, + "WorldParam": { + "Type": "String", + "Default": "world" + } + }, + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Resources": { + "TrueRef": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "TrueRefRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueRefVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "TrueRef" + } + } + }, + "TrueRefAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "TrueRefDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "TrueRef" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "TrueRefVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "TrueRefRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseRef": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FalseRefRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseRefVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "FalseRef" + } + } + }, + "FalseRefAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "FalseRefDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "FalseRef" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "FalseRefVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "FalseRefRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueFindInMap": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "TrueFindInMapRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueFindInMapVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "TrueFindInMap" + } + } + }, + "TrueFindInMapAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "TrueFindInMapDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "TrueFindInMap" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "TrueFindInMapVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "TrueFindInMapRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseFindInMap": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FalseFindInMapRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseFindInMapVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "FalseFindInMap" + } + } + }, + "FalseFindInMapAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "FalseFindInMapDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "FalseFindInMap" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "FalseFindInMapVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "FalseFindInMapRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ] + } + }, + "TrueRefDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Condition": "FunctionCondition", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "FalseRefDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "TrueFindInMapDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Condition": "FunctionCondition", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "FalseFindInMapDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_function_url_config_and_autopublishalias.json b/tests/translator/output/aws-cn/function_with_function_url_config_and_autopublishalias.json index 5e0d8c2e1..155784e64 100644 --- a/tests/translator/output/aws-cn/function_with_function_url_config_and_autopublishalias.json +++ b/tests/translator/output/aws-cn/function_with_function_url_config_and_autopublishalias.json @@ -85,7 +85,7 @@ "Properties": { "Action": "lambda:InvokeFunctionUrl", "FunctionName": { - "Ref": "MyFunction" + "Ref": "MyFunctionAliaslive" }, "Principal": "*", "FunctionUrlAuthType": "NONE" diff --git a/tests/translator/output/aws-cn/function_with_function_url_config_conditions.json b/tests/translator/output/aws-cn/function_with_function_url_config_conditions.json new file mode 100644 index 000000000..64d3346dd --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_function_url_config_conditions.json @@ -0,0 +1,113 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Conditions": { + "MyCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": {}, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "MyCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "Created by SAM", + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Timeout": 3, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionUrl": { + "Type": "AWS::Lambda::Url", + "Condition": "MyCondition", + "Properties": { + "TargetFunctionArn": { + "Ref": "MyFunction" + }, + "AuthType": "NONE", + "Cors": { + "AllowOrigins": [ + "https://example.com", + "example1.com", + "example2.com", + "example2.com" + ], + "AllowMethods": [ + "GET" + ], + "AllowCredentials": true, + "AllowHeaders": [ + "x-Custom-Header" + ], + "ExposeHeaders": [ + "x-amzn-header" + ], + "MaxAge": 10 + } + } + }, + "MyFunctionUrlPublicPermissions": { + "Type": "AWS::Lambda::Permission", + "Condition": "MyCondition", + "Properties": { + "Action": "lambda:InvokeFunctionUrl", + "FunctionName": { + "Ref": "MyFunction" + }, + "Principal": "*", + "FunctionUrlAuthType": "NONE" + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "MyCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/http_api_custom_iam_auth.json b/tests/translator/output/aws-cn/http_api_custom_iam_auth.json new file mode 100644 index 000000000..295aab3d8 --- /dev/null +++ b/tests/translator/output/aws-cn/http_api_custom_iam_auth.json @@ -0,0 +1,137 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionHelloAWSIAMAuthPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "MyApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/hello-aws-iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/hello-aws-iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { "name": "Tag1", "x-amazon-apigateway-tag-value": "value1" }, + { "name": "Tag2", "x-amazon-apigateway-tag-value": "value2" }, + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { + "Tag1": "value1", + "Tag2": "value2", + "httpapi:createdBy": "SAM" + } + }, + "Type": "AWS::ApiGatewayV2::Stage" + }, + "MyAuthFn": { + "Properties": { + "Code": { "S3Bucket": "bucket", "S3Key": "key" }, + "Handler": "index.handler", + "Role": { "Fn::GetAtt": ["Arn", "MyAuthFnRole"] }, + "Runtime": "nodejs12.x", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "MyAuthFnRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/http_api_global_iam_auth_enabled.json b/tests/translator/output/aws-cn/http_api_global_iam_auth_enabled.json new file mode 100644 index 000000000..4b0504085 --- /dev/null +++ b/tests/translator/output/aws-cn/http_api_global_iam_auth_enabled.json @@ -0,0 +1,124 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionImplicitApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "ServerlessHttpApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionImplicitApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "ServerlessHttpApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "ServerlessHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "ServerlessHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "ServerlessHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-cn/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json b/tests/translator/output/aws-cn/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json new file mode 100644 index 000000000..ceea480e8 --- /dev/null +++ b/tests/translator/output/aws-cn/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json @@ -0,0 +1,44 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": {}, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-cn/http_api_local_iam_auth_enabled.json b/tests/translator/output/aws-cn/http_api_local_iam_auth_enabled.json new file mode 100644 index 000000000..6cf579d13 --- /dev/null +++ b/tests/translator/output/aws-cn/http_api_local_iam_auth_enabled.json @@ -0,0 +1,259 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiNoAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/no-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyIamAuthEnabledHttpApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyIamAuthEnabledHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyIamAuthEnabledHttpApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyIamAuthEnabledHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "MyDefaultIamAuthHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/no-auth": { + "get": { + "responses": {}, + "security": [{ "NONE": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyDefaultIamAuthHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyDefaultIamAuthHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + }, + "MyIamAuthEnabledHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyIamAuthEnabledHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyIamAuthEnabledHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-cn/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json b/tests/translator/output/aws-cn/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json new file mode 100644 index 000000000..ceea480e8 --- /dev/null +++ b/tests/translator/output/aws-cn/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json @@ -0,0 +1,44 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": {}, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-cn/http_api_multiple_authorizers.json b/tests/translator/output/aws-cn/http_api_multiple_authorizers.json index 97e5c2493..a42dc5f8a 100644 --- a/tests/translator/output/aws-cn/http_api_multiple_authorizers.json +++ b/tests/translator/output/aws-cn/http_api_multiple_authorizers.json @@ -9,10 +9,7 @@ }, "Handler": "index.restapi", "Role": { - "Fn::GetAtt": [ - "HttpApiFunctionRole", - "Arn" - ] + "Fn::GetAtt": ["HttpApiFunctionRole", "Arn"] }, "Runtime": "python3.7", "Tags": [ @@ -30,14 +27,10 @@ "Version": "2012-10-17", "Statement": [ { - "Action": [ - "sts:AssumeRole" - ], + "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] + "Service": ["lambda.amazonaws.com"] } } ] @@ -83,10 +76,7 @@ }, "Handler": "index.handler", "Role": { - "Fn::GetAtt": [ - "MyAuthFnRole", - "Arn" - ] + "Fn::GetAtt": ["MyAuthFnRole", "Arn"] }, "Runtime": "nodejs12.x", "Tags": [ @@ -104,14 +94,10 @@ "Version": "2012-10-17", "Statement": [ { - "Action": [ - "sts:AssumeRole" - ], + "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] + "Service": ["lambda.amazonaws.com"] } } ] @@ -168,9 +154,7 @@ "responses": {}, "security": [ { - "MyOauth2Authorizer": [ - "scope" - ] + "MyOauth2Authorizer": ["scope"] } ] }, @@ -191,6 +175,24 @@ ] } }, + "/hello-aws-iam-auth": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ] + } + }, "$default": { "x-amazon-apigateway-any-method": { "x-amazon-apigateway-integration": { @@ -213,6 +215,12 @@ }, "components": { "securitySchemes": { + "AWS_IAM": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4" + }, "LambdaAuth": { "type": "apiKey", "name": "Unused", @@ -224,10 +232,7 @@ "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", { "__FunctionArn__": { - "Fn::GetAtt": [ - "MyAuthFn", - "Arn" - ] + "Fn::GetAtt": ["MyAuthFn", "Arn"] } } ] @@ -239,10 +244,7 @@ "type": "oauth2", "x-amazon-apigateway-authorizer": { "jwtConfiguration": { - "audience": [ - "audience1", - "audience2" - ], + "audience": ["audience1", "audience2"], "issuer": "https://www.example.com/v1/connect/oidc" }, "identitySource": "$request.header.Authorization", @@ -284,4 +286,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/translator/output/aws-us-gov/api_with_apikey_source.json b/tests/translator/output/aws-us-gov/api_with_apikey_source.json new file mode 100644 index 000000000..10e847875 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_apikey_source.json @@ -0,0 +1,149 @@ +{ + "Resources": { + "MyFunctionWithApiKeyRequiredTrueMyApiWithApiKeyRequiredTruePermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithApiKeyRequiredTrue" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/ApiKeyTrue", + { + "__ApiId__": { + "Ref": "MyApiWithAuthSource" + }, + "__Stage__": "*" + } + ] + } + } + }, + "MyApiWithAuthSource": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/ApiKeyTrue": { + "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/${MyFunctionWithApiKeyRequiredTrue.Arn}/invocations" + } + }, + "security": [ + { + "api_key": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "x-api-key", + "in": "header" + } + } + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "ApiKeySourceType": "AUTHORIZER" + } + }, + "MyFunctionWithApiKeyRequiredTrue": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionWithApiKeyRequiredTrueRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyApiWithAuthSourceDeployment9bfe55913d": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "RestApi deployment id: 9bfe55913df8648e4e4fa59c5a88e49ee8f6b5b7", + "RestApiId": { + "Ref": "MyApiWithAuthSource" + }, + "StageName": "Stage" + } + }, + "MyFunctionWithApiKeyRequiredTrueRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyApiWithAuthSourceProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithAuthSourceDeployment9bfe55913d" + }, + "RestApiId": { + "Ref": "MyApiWithAuthSource" + }, + "StageName": "Prod" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_deployment_no_service_role_with_passthrough.json b/tests/translator/output/aws-us-gov/function_with_deployment_no_service_role_with_passthrough.json new file mode 100644 index 000000000..8300aab2f --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_deployment_no_service_role_with_passthrough.json @@ -0,0 +1,283 @@ +{ + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Resources": { + "OtherFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Ref": "DeploymentRole" + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "AllAtOnce" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition1" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MinimalFunctionRole": { + "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" + } + ] + } + }, + "OtherFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "OtherFunction" + } + }, + "Condition": "Condition1" + }, + "OtherFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition1" + }, + "DeploymentRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + } + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Ref": "DeploymentRole" + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "AllAtOnce" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + } + }, + "OtherFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "OtherFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + }, + "Name": "live" + } + }, + "OtherFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "OtherFunctionDeploymentGroup" + } + } + }, + "Condition": "Condition1", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "OtherFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "OtherFunction" + }, + "Name": "live" + } + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_deployment_no_service_role.json b/tests/translator/output/aws-us-gov/function_with_deployment_no_service_role_without_passthrough.json similarity index 67% rename from tests/translator/output/aws-us-gov/function_with_deployment_no_service_role.json rename to tests/translator/output/aws-us-gov/function_with_deployment_no_service_role_without_passthrough.json index ca6cf4a1e..ad07002d0 100644 --- a/tests/translator/output/aws-us-gov/function_with_deployment_no_service_role.json +++ b/tests/translator/output/aws-us-gov/function_with_deployment_no_service_role_without_passthrough.json @@ -1,79 +1,78 @@ { + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, "Resources": { "OtherFunctionDeploymentGroup": { - "Type": "AWS::CodeDeploy::DeploymentGroup", + "Type": "AWS::CodeDeploy::DeploymentGroup", "Properties": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "AutoRollbackConfiguration": { - "Enabled": true, + "Enabled": true, "Events": [ - "DEPLOYMENT_FAILURE", - "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST" ] - }, + }, "ServiceRoleArn": { "Ref": "DeploymentRole" - }, + }, "DeploymentConfigName": { "Fn::Sub": [ - "CodeDeployDefault.Lambda${ConfigName}", + "CodeDeployDefault.Lambda${ConfigName}", { "ConfigName": "AllAtOnce" } ] - }, + }, "DeploymentStyle": { - "DeploymentType": "BLUE_GREEN", + "DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL" } } - }, + }, "MinimalFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { + "Handler": "hello.handler", "Code": { - "S3Bucket": "sam-demo-bucket", + "S3Bucket": "sam-demo-bucket", "S3Key": "hello.zip" - }, - "Handler": "hello.handler", + }, "Role": { "Fn::GetAtt": [ - "MinimalFunctionRole", + "MinimalFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", + }, + "Runtime": "python2.7", "Tags": [ { - "Value": "SAM", + "Value": "SAM", "Key": "lambda:createdBy" } ] } - }, + }, "MinimalFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" @@ -81,21 +80,7 @@ } } ] - } - } - }, - "OtherFunctionVersion640128d35d": { - "DeletionPolicy": "Retain", - "Type": "AWS::Lambda::Version", - "Properties": { - "FunctionName": { - "Ref": "OtherFunction" - } - } - }, - "OtherFunctionRole": { - "Type": "AWS::IAM::Role", - "Properties": { + }, "ManagedPolicyArns": [ "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ], @@ -104,15 +89,30 @@ "Value": "SAM", "Key": "lambda:createdBy" } - ], + ] + } + }, + "OtherFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "OtherFunction" + } + }, + "Condition": "Condition1" + }, + "OtherFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" @@ -120,23 +120,33 @@ } } ] - } - } - }, + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, "DeploymentRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "codedeploy.amazonaws.com" @@ -146,125 +156,127 @@ ] } } - }, + }, "MinimalFunctionDeploymentGroup": { - "Type": "AWS::CodeDeploy::DeploymentGroup", + "Type": "AWS::CodeDeploy::DeploymentGroup", "Properties": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "AutoRollbackConfiguration": { - "Enabled": true, + "Enabled": true, "Events": [ - "DEPLOYMENT_FAILURE", - "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST" ] - }, + }, "ServiceRoleArn": { "Ref": "DeploymentRole" - }, + }, "DeploymentConfigName": { "Fn::Sub": [ - "CodeDeployDefault.Lambda${ConfigName}", + "CodeDeployDefault.Lambda${ConfigName}", { "ConfigName": "AllAtOnce" } ] - }, + }, "DeploymentStyle": { - "DeploymentType": "BLUE_GREEN", + "DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL" } } - }, + }, "ServerlessDeploymentApplication": { - "Type": "AWS::CodeDeploy::Application", + "Type": "AWS::CodeDeploy::Application", "Properties": { "ComputePlatform": "Lambda" } - }, + }, "MinimalFunctionVersion640128d35d": { - "DeletionPolicy": "Retain", - "Type": "AWS::Lambda::Version", + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { "Ref": "MinimalFunction" } } - }, + }, "OtherFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { + "Handler": "hello.handler", "Code": { - "S3Bucket": "sam-demo-bucket", + "S3Bucket": "sam-demo-bucket", "S3Key": "hello.zip" - }, - "Handler": "hello.handler", + }, "Role": { "Fn::GetAtt": [ - "OtherFunctionRole", + "OtherFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", + }, + "Runtime": "python2.7", "Tags": [ { - "Value": "SAM", + "Value": "SAM", "Key": "lambda:createdBy" } ] - } - }, + }, + "Condition": "Condition1" + }, "MinimalFunctionAliaslive": { - "Type": "AWS::Lambda::Alias", + "Type": "AWS::Lambda::Alias", "UpdatePolicy": { "CodeDeployLambdaAliasUpdate": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "DeploymentGroupName": { "Ref": "MinimalFunctionDeploymentGroup" } } - }, + }, "Properties": { "FunctionVersion": { "Fn::GetAtt": [ - "MinimalFunctionVersion640128d35d", + "MinimalFunctionVersion640128d35d", "Version" ] - }, + }, "FunctionName": { "Ref": "MinimalFunction" - }, + }, "Name": "live" } - }, + }, "OtherFunctionAliaslive": { - "Type": "AWS::Lambda::Alias", + "Type": "AWS::Lambda::Alias", "UpdatePolicy": { "CodeDeployLambdaAliasUpdate": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "DeploymentGroupName": { "Ref": "OtherFunctionDeploymentGroup" } } - }, + }, + "Condition": "Condition1", "Properties": { "FunctionVersion": { "Fn::GetAtt": [ - "OtherFunctionVersion640128d35d", + "OtherFunctionVersion640128d35d", "Version" ] - }, + }, "FunctionName": { "Ref": "OtherFunction" - }, + }, "Name": "live" } } } -} \ No newline at end of file +} diff --git a/tests/translator/output/aws-us-gov/function_with_deployment_preference_condition_with_passthrough.json b/tests/translator/output/aws-us-gov/function_with_deployment_preference_condition_with_passthrough.json new file mode 100644 index 000000000..385a1f49f --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_deployment_preference_condition_with_passthrough.json @@ -0,0 +1,208 @@ +{ + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "EnableAliasProvisionedConcurrency": { + "Default": true, + "Type": "String", + "AllowedValues": [ + true, + false + ] + }, + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Default": 10, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "FunctionCondition" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "FunctionCondition" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "FunctionCondition" + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "FunctionCondition", + "Properties": { + "Name": "live", + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + } + } + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_deployment_preference_condition_without_passthrough.json b/tests/translator/output/aws-us-gov/function_with_deployment_preference_condition_without_passthrough.json new file mode 100644 index 000000000..d69bc3b59 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_deployment_preference_condition_without_passthrough.json @@ -0,0 +1,205 @@ +{ + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "EnableAliasProvisionedConcurrency": { + "Default": true, + "Type": "String", + "AllowedValues": [ + true, + false + ] + }, + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Default": 10, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + } + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "FunctionCondition" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "FunctionCondition" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "FunctionCondition", + "Properties": { + "Name": "live", + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + } + } + } + } + } \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json b/tests/translator/output/aws-us-gov/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json new file mode 100644 index 000000000..d534f9d6f --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json @@ -0,0 +1,585 @@ +{ + "Conditions": { + "ServerlessCodeDeployCondition": { + "Fn::Or": [ + { + "Condition": "Condition2" + }, + { + "Condition": "Condition1" + } + ] + }, + "Condition3": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition2": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "MyFalseParameter": { + "Default": false, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, + "MyCloudWatchAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "EvaluationPeriods": 1, + "Namespace": "AWS/EC2", + "Period": 300, + "ComparisonOperator": "GreaterThanThreshold", + "Threshold": 10, + "MetricName": "MyMetric" + } + }, + "MinimalFunctionWithMinimalDeploymentPreference": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition1" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + } + }, + "Condition": "Condition2" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "ServerlessCodeDeployCondition" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Canary10Percent5Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsAliaslivewithdeploymentwithhooksandalarms": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "BeforeAllowTrafficHook": { + "Ref": "MySanityTestFunction" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup" + }, + "AfterAllowTrafficHook": { + "Ref": "MyValidationTestFunction" + } + } + }, + "Condition": "Condition2", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + }, + "Name": "livewithdeploymentwithhooksandalarms" + } + }, + "MyValidationTestFunctionRole": { + "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" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceRole": { + "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" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition1" + }, + "MySanityTestFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition3" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "AlarmConfiguration": { + "Alarms": [ + { + "Name": { + "Ref": "MyCloudWatchAlarm" + } + } + ], + "Enabled": true + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole": { + "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" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceAliaslivewithdeployment": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup" + } + } + }, + "Condition": "Condition2", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + }, + "Name": "livewithdeployment" + } + }, + "MySanityTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "mySanityTestFunction.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MySanityTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition3" + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + }, + "Condition": "ServerlessCodeDeployCondition" + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "Condition1" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "Condition1", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + }, + "Name": "live" + } + }, + "MyValidationTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "myValidationTestFunction.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MyValidationTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json b/tests/translator/output/aws-us-gov/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json new file mode 100644 index 000000000..aaf023072 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json @@ -0,0 +1,570 @@ +{ + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + }, + "Condition2": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition3": { + "Fn::Equals": [ + true, + false + ] + } + }, + "Parameters": { + "MyFalseParameter": { + "Type": "String", + "Default": false + } + }, + "Resources": { + "MyCloudWatchAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "MetricName": "MyMetric", + "Namespace": "AWS/EC2", + "Period": 300, + "Threshold": 10 + } + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition1", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition1", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + } + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition1", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "MinimalFunction" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition1", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreference": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition2", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition2", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceAliaslivewithdeployment": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition2", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup" + } + } + }, + "Properties": { + "Name": "livewithdeployment", + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition2", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition2", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition2", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsAliaslivewithdeploymentwithhooksandalarms": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition2", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup" + }, + "BeforeAllowTrafficHook": { + "Ref": "MySanityTestFunction" + }, + "AfterAllowTrafficHook": { + "Ref": "MyValidationTestFunction" + } + } + }, + "Properties": { + "Name": "livewithdeploymentwithhooksandalarms", + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition2", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MySanityTestFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition3", + "Properties": { + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "mySanityTestFunction.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MySanityTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MySanityTestFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition3", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyValidationTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "myValidationTestFunction.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MyValidationTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyValidationTestFunctionRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ] + } + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Canary10Percent5Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "AlarmConfiguration": { + "Enabled": true, + "Alarms": [ + { + "Name": { + "Ref": "MyCloudWatchAlarm" + } + } + ] + }, + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json b/tests/translator/output/aws-us-gov/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json new file mode 100644 index 000000000..05b248c26 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json @@ -0,0 +1,649 @@ +{ + "Mappings": { + "HelloWorldMap": { + "hello": { + "key1": true, + "key2": false + }, + "world": { + "key1": false, + "key2": true + } + } + }, + "Parameters": { + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Type": "String", + "Default": 10 + }, + "EnableAliasProvisionedConcurrency": { + "Type": "String", + "AllowedValues": [ + true, + false + ], + "Default": true + }, + "DefaultTrueParam": { + "Type": "String", + "Default": "true" + }, + "DefaultFalseParam": { + "Type": "String", + "Default": "false" + }, + "HelloParam": { + "Type": "String", + "Default": "hello" + }, + "WorldParam": { + "Type": "String", + "Default": "world" + } + }, + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Resources": { + "TrueRef": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "TrueRefRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueRefVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "TrueRef" + } + } + }, + "TrueRefAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "TrueRefDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "TrueRef" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "TrueRefVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "TrueRefRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseRef": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FalseRefRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseRefVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "FalseRef" + } + } + }, + "FalseRefAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "FalseRefDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "FalseRef" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "FalseRefVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "FalseRefRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueFindInMap": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "TrueFindInMapRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueFindInMapVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "TrueFindInMap" + } + } + }, + "TrueFindInMapAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "TrueFindInMapDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "TrueFindInMap" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "TrueFindInMapVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "TrueFindInMapRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseFindInMap": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FalseFindInMapRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseFindInMapVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "FalseFindInMap" + } + } + }, + "FalseFindInMapAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "FalseFindInMapDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "FalseFindInMap" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "FalseFindInMapVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "FalseFindInMapRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ] + } + }, + "TrueRefDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Condition": "FunctionCondition", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "FalseRefDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "TrueFindInMapDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Condition": "FunctionCondition", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "FalseFindInMapDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_function_url_config_and_autopublishalias.json b/tests/translator/output/aws-us-gov/function_with_function_url_config_and_autopublishalias.json index 9d3a10312..3714b1289 100644 --- a/tests/translator/output/aws-us-gov/function_with_function_url_config_and_autopublishalias.json +++ b/tests/translator/output/aws-us-gov/function_with_function_url_config_and_autopublishalias.json @@ -85,7 +85,7 @@ "Properties": { "Action": "lambda:InvokeFunctionUrl", "FunctionName": { - "Ref": "MyFunction" + "Ref": "MyFunctionAliaslive" }, "Principal": "*", "FunctionUrlAuthType": "NONE" diff --git a/tests/translator/output/aws-us-gov/function_with_function_url_config_conditions.json b/tests/translator/output/aws-us-gov/function_with_function_url_config_conditions.json new file mode 100644 index 000000000..503d8d4f7 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_function_url_config_conditions.json @@ -0,0 +1,113 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Conditions": { + "MyCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": {}, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "MyCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "Created by SAM", + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Timeout": 3, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionUrl": { + "Type": "AWS::Lambda::Url", + "Condition": "MyCondition", + "Properties": { + "TargetFunctionArn": { + "Ref": "MyFunction" + }, + "AuthType": "NONE", + "Cors": { + "AllowOrigins": [ + "https://example.com", + "example1.com", + "example2.com", + "example2.com" + ], + "AllowMethods": [ + "GET" + ], + "AllowCredentials": true, + "AllowHeaders": [ + "x-Custom-Header" + ], + "ExposeHeaders": [ + "x-amzn-header" + ], + "MaxAge": 10 + } + } + }, + "MyFunctionUrlPublicPermissions": { + "Type": "AWS::Lambda::Permission", + "Condition": "MyCondition", + "Properties": { + "Action": "lambda:InvokeFunctionUrl", + "FunctionName": { + "Ref": "MyFunction" + }, + "Principal": "*", + "FunctionUrlAuthType": "NONE" + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "MyCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/http_api_custom_iam_auth.json b/tests/translator/output/aws-us-gov/http_api_custom_iam_auth.json new file mode 100644 index 000000000..326678522 --- /dev/null +++ b/tests/translator/output/aws-us-gov/http_api_custom_iam_auth.json @@ -0,0 +1,137 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionHelloAWSIAMAuthPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "MyApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/hello-aws-iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/hello-aws-iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { "name": "Tag1", "x-amazon-apigateway-tag-value": "value1" }, + { "name": "Tag2", "x-amazon-apigateway-tag-value": "value2" }, + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { + "Tag1": "value1", + "Tag2": "value2", + "httpapi:createdBy": "SAM" + } + }, + "Type": "AWS::ApiGatewayV2::Stage" + }, + "MyAuthFn": { + "Properties": { + "Code": { "S3Bucket": "bucket", "S3Key": "key" }, + "Handler": "index.handler", + "Role": { "Fn::GetAtt": ["Arn", "MyAuthFnRole"] }, + "Runtime": "nodejs12.x", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "MyAuthFnRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/http_api_global_iam_auth_enabled.json b/tests/translator/output/aws-us-gov/http_api_global_iam_auth_enabled.json new file mode 100644 index 000000000..de3f06ee5 --- /dev/null +++ b/tests/translator/output/aws-us-gov/http_api_global_iam_auth_enabled.json @@ -0,0 +1,124 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionImplicitApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "ServerlessHttpApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionImplicitApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "ServerlessHttpApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "ServerlessHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "ServerlessHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "ServerlessHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-us-gov/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json b/tests/translator/output/aws-us-gov/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json new file mode 100644 index 000000000..ceea480e8 --- /dev/null +++ b/tests/translator/output/aws-us-gov/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json @@ -0,0 +1,44 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": {}, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-us-gov/http_api_local_iam_auth_enabled.json b/tests/translator/output/aws-us-gov/http_api_local_iam_auth_enabled.json new file mode 100644 index 000000000..c48858833 --- /dev/null +++ b/tests/translator/output/aws-us-gov/http_api_local_iam_auth_enabled.json @@ -0,0 +1,259 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiNoAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/no-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyIamAuthEnabledHttpApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyIamAuthEnabledHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyIamAuthEnabledHttpApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyIamAuthEnabledHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "MyDefaultIamAuthHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/no-auth": { + "get": { + "responses": {}, + "security": [{ "NONE": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyDefaultIamAuthHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyDefaultIamAuthHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + }, + "MyIamAuthEnabledHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyIamAuthEnabledHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyIamAuthEnabledHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-us-gov/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json b/tests/translator/output/aws-us-gov/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json new file mode 100644 index 000000000..ceea480e8 --- /dev/null +++ b/tests/translator/output/aws-us-gov/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json @@ -0,0 +1,44 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": {}, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/aws-us-gov/http_api_multiple_authorizers.json b/tests/translator/output/aws-us-gov/http_api_multiple_authorizers.json index cf32096be..3f705ca93 100644 --- a/tests/translator/output/aws-us-gov/http_api_multiple_authorizers.json +++ b/tests/translator/output/aws-us-gov/http_api_multiple_authorizers.json @@ -9,10 +9,7 @@ }, "Handler": "index.restapi", "Role": { - "Fn::GetAtt": [ - "HttpApiFunctionRole", - "Arn" - ] + "Fn::GetAtt": ["HttpApiFunctionRole", "Arn"] }, "Runtime": "python3.7", "Tags": [ @@ -30,14 +27,10 @@ "Version": "2012-10-17", "Statement": [ { - "Action": [ - "sts:AssumeRole" - ], + "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] + "Service": ["lambda.amazonaws.com"] } } ] @@ -83,10 +76,7 @@ }, "Handler": "index.handler", "Role": { - "Fn::GetAtt": [ - "MyAuthFnRole", - "Arn" - ] + "Fn::GetAtt": ["MyAuthFnRole", "Arn"] }, "Runtime": "nodejs12.x", "Tags": [ @@ -104,14 +94,10 @@ "Version": "2012-10-17", "Statement": [ { - "Action": [ - "sts:AssumeRole" - ], + "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] + "Service": ["lambda.amazonaws.com"] } } ] @@ -168,9 +154,7 @@ "responses": {}, "security": [ { - "MyOauth2Authorizer": [ - "scope" - ] + "MyOauth2Authorizer": ["scope"] } ] }, @@ -191,6 +175,24 @@ ] } }, + "/hello-aws-iam-auth": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ] + } + }, "$default": { "x-amazon-apigateway-any-method": { "x-amazon-apigateway-integration": { @@ -213,6 +215,12 @@ }, "components": { "securitySchemes": { + "AWS_IAM": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4" + }, "LambdaAuth": { "type": "apiKey", "name": "Unused", @@ -224,10 +232,7 @@ "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", { "__FunctionArn__": { - "Fn::GetAtt": [ - "MyAuthFn", - "Arn" - ] + "Fn::GetAtt": ["MyAuthFn", "Arn"] } } ] @@ -239,10 +244,7 @@ "type": "oauth2", "x-amazon-apigateway-authorizer": { "jwtConfiguration": { - "audience": [ - "audience1", - "audience2" - ], + "audience": ["audience1", "audience2"], "issuer": "https://www.example.com/v1/connect/oidc" }, "identitySource": "$request.header.Authorization", @@ -284,4 +286,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/translator/output/error_api_with_models_of_invalid_type.json b/tests/translator/output/error_api_with_models_of_invalid_type.json new file mode 100644 index 000000000..1f00f5733 --- /dev/null +++ b/tests/translator/output/error_api_with_models_of_invalid_type.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 2. Resource with id [MyApi] is invalid. Type of property 'Models' is invalid. Resource with id [MyFunction] is invalid. Event with id [None] is invalid. Unable to set RequestModel [User] on API method [get] for path [/none] because the related API Models defined is of invalid type." +} \ No newline at end of file diff --git a/tests/translator/output/error_function_api_invalid_properties.json b/tests/translator/output/error_function_api_invalid_properties.json new file mode 100644 index 000000000..7208a9047 --- /dev/null +++ b/tests/translator/output/error_function_api_invalid_properties.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [Function] is invalid. Event with id [Api] is invalid. Event 'Properties' must be an Object. If you're using YAML, this may be an indentation issue." +} \ No newline at end of file diff --git a/tests/translator/output/error_function_with_deployment_preference_passthrough_condition_with_invalid_values.json b/tests/translator/output/error_function_with_deployment_preference_passthrough_condition_with_invalid_values.json new file mode 100644 index 000000000..931b1c7d2 --- /dev/null +++ b/tests/translator/output/error_function_with_deployment_preference_passthrough_condition_with_invalid_values.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 2. Resource with id [InvalidType] is invalid. Invalid value for property PassthroughCondition. Resource with id [UnsupportedIntrinsic] is invalid. Unsupported intrinsic: the only intrinsic functions supported for property PassthroughCondition are FindInMap and parameter Refs." +} \ No newline at end of file diff --git a/tests/translator/output/error_function_with_invalid_kms_type_for_self_managed_kafka.json b/tests/translator/output/error_function_with_invalid_kms_type_for_self_managed_kafka.json new file mode 100644 index 000000000..67bfd9cf1 --- /dev/null +++ b/tests/translator/output/error_function_with_invalid_kms_type_for_self_managed_kafka.json @@ -0,0 +1,8 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Provided SecretsManagerKmsKeyId should be of type str.", + "errors": [ + { + "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Provided SecretsManagerKmsKeyId should be of type str." + } + ] +} \ No newline at end of file diff --git a/tests/translator/output/error_function_with_mq_kms_invalid_type.json b/tests/translator/output/error_function_with_mq_kms_invalid_type.json new file mode 100644 index 000000000..268fb6354 --- /dev/null +++ b/tests/translator/output/error_function_with_mq_kms_invalid_type.json @@ -0,0 +1,8 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Provided SecretsManagerKmsKeyId should be of type str.", + "errors": [ + { + "errorMessage": "Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Provided SecretsManagerKmsKeyId should be of type str." + } + ] +} \ No newline at end of file diff --git a/tests/translator/output/function_with_deployment_no_service_role_with_passthrough.json b/tests/translator/output/function_with_deployment_no_service_role_with_passthrough.json new file mode 100644 index 000000000..1a6c407f6 --- /dev/null +++ b/tests/translator/output/function_with_deployment_no_service_role_with_passthrough.json @@ -0,0 +1,283 @@ +{ + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Resources": { + "OtherFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Ref": "DeploymentRole" + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "AllAtOnce" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition1" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "MinimalFunctionRole": { + "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" + } + ] + } + }, + "OtherFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "OtherFunction" + } + }, + "Condition": "Condition1" + }, + "OtherFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition1" + }, + "DeploymentRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + } + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Ref": "DeploymentRole" + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "AllAtOnce" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + } + }, + "OtherFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "OtherFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + }, + "Name": "live" + } + }, + "OtherFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "OtherFunctionDeploymentGroup" + } + } + }, + "Condition": "Condition1", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "OtherFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "OtherFunction" + }, + "Name": "live" + } + } + } +} diff --git a/tests/translator/output/function_with_deployment_no_service_role.json b/tests/translator/output/function_with_deployment_no_service_role_without_passthrough.json similarity index 67% rename from tests/translator/output/function_with_deployment_no_service_role.json rename to tests/translator/output/function_with_deployment_no_service_role_without_passthrough.json index ef9ed4aa6..e3b229376 100644 --- a/tests/translator/output/function_with_deployment_no_service_role.json +++ b/tests/translator/output/function_with_deployment_no_service_role_without_passthrough.json @@ -1,79 +1,78 @@ { + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, "Resources": { "OtherFunctionDeploymentGroup": { - "Type": "AWS::CodeDeploy::DeploymentGroup", + "Type": "AWS::CodeDeploy::DeploymentGroup", "Properties": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "AutoRollbackConfiguration": { - "Enabled": true, + "Enabled": true, "Events": [ - "DEPLOYMENT_FAILURE", - "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST" ] - }, + }, "ServiceRoleArn": { "Ref": "DeploymentRole" - }, + }, "DeploymentConfigName": { "Fn::Sub": [ - "CodeDeployDefault.Lambda${ConfigName}", + "CodeDeployDefault.Lambda${ConfigName}", { "ConfigName": "AllAtOnce" } ] - }, + }, "DeploymentStyle": { - "DeploymentType": "BLUE_GREEN", + "DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL" } } - }, + }, "MinimalFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { + "Handler": "hello.handler", "Code": { - "S3Bucket": "sam-demo-bucket", + "S3Bucket": "sam-demo-bucket", "S3Key": "hello.zip" - }, - "Handler": "hello.handler", + }, "Role": { "Fn::GetAtt": [ - "MinimalFunctionRole", + "MinimalFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", + }, + "Runtime": "python2.7", "Tags": [ { - "Value": "SAM", + "Value": "SAM", "Key": "lambda:createdBy" } ] } - }, + }, "MinimalFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ], - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" @@ -81,21 +80,7 @@ } } ] - } - } - }, - "OtherFunctionVersion640128d35d": { - "DeletionPolicy": "Retain", - "Type": "AWS::Lambda::Version", - "Properties": { - "FunctionName": { - "Ref": "OtherFunction" - } - } - }, - "OtherFunctionRole": { - "Type": "AWS::IAM::Role", - "Properties": { + }, "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ], @@ -104,15 +89,30 @@ "Value": "SAM", "Key": "lambda:createdBy" } - ], + ] + } + }, + "OtherFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "OtherFunction" + } + }, + "Condition": "Condition1" + }, + "OtherFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" @@ -120,23 +120,33 @@ } } ] - } - } - }, + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, "DeploymentRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", + "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" - ], - "Effect": "Allow", + ], + "Effect": "Allow", "Principal": { "Service": [ "codedeploy.amazonaws.com" @@ -146,125 +156,127 @@ ] } } - }, + }, "MinimalFunctionDeploymentGroup": { - "Type": "AWS::CodeDeploy::DeploymentGroup", + "Type": "AWS::CodeDeploy::DeploymentGroup", "Properties": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "AutoRollbackConfiguration": { - "Enabled": true, + "Enabled": true, "Events": [ - "DEPLOYMENT_FAILURE", - "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", "DEPLOYMENT_STOP_ON_REQUEST" ] - }, + }, "ServiceRoleArn": { "Ref": "DeploymentRole" - }, + }, "DeploymentConfigName": { "Fn::Sub": [ - "CodeDeployDefault.Lambda${ConfigName}", + "CodeDeployDefault.Lambda${ConfigName}", { "ConfigName": "AllAtOnce" } ] - }, + }, "DeploymentStyle": { - "DeploymentType": "BLUE_GREEN", + "DeploymentType": "BLUE_GREEN", "DeploymentOption": "WITH_TRAFFIC_CONTROL" } } - }, + }, "ServerlessDeploymentApplication": { - "Type": "AWS::CodeDeploy::Application", + "Type": "AWS::CodeDeploy::Application", "Properties": { "ComputePlatform": "Lambda" } - }, + }, "MinimalFunctionVersion640128d35d": { - "DeletionPolicy": "Retain", - "Type": "AWS::Lambda::Version", + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", "Properties": { "FunctionName": { "Ref": "MinimalFunction" } } - }, + }, "OtherFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { + "Handler": "hello.handler", "Code": { - "S3Bucket": "sam-demo-bucket", + "S3Bucket": "sam-demo-bucket", "S3Key": "hello.zip" - }, - "Handler": "hello.handler", + }, "Role": { "Fn::GetAtt": [ - "OtherFunctionRole", + "OtherFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", + }, + "Runtime": "python2.7", "Tags": [ { - "Value": "SAM", + "Value": "SAM", "Key": "lambda:createdBy" } ] - } - }, + }, + "Condition": "Condition1" + }, "MinimalFunctionAliaslive": { - "Type": "AWS::Lambda::Alias", + "Type": "AWS::Lambda::Alias", "UpdatePolicy": { "CodeDeployLambdaAliasUpdate": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "DeploymentGroupName": { "Ref": "MinimalFunctionDeploymentGroup" } } - }, + }, "Properties": { "FunctionVersion": { "Fn::GetAtt": [ - "MinimalFunctionVersion640128d35d", + "MinimalFunctionVersion640128d35d", "Version" ] - }, + }, "FunctionName": { "Ref": "MinimalFunction" - }, + }, "Name": "live" } - }, + }, "OtherFunctionAliaslive": { - "Type": "AWS::Lambda::Alias", + "Type": "AWS::Lambda::Alias", "UpdatePolicy": { "CodeDeployLambdaAliasUpdate": { "ApplicationName": { "Ref": "ServerlessDeploymentApplication" - }, + }, "DeploymentGroupName": { "Ref": "OtherFunctionDeploymentGroup" } } - }, + }, + "Condition": "Condition1", "Properties": { "FunctionVersion": { "Fn::GetAtt": [ - "OtherFunctionVersion640128d35d", + "OtherFunctionVersion640128d35d", "Version" ] - }, + }, "FunctionName": { "Ref": "OtherFunction" - }, + }, "Name": "live" } } } -} \ No newline at end of file +} diff --git a/tests/translator/output/function_with_deployment_preference_condition_with_passthrough.json b/tests/translator/output/function_with_deployment_preference_condition_with_passthrough.json new file mode 100644 index 000000000..07936044d --- /dev/null +++ b/tests/translator/output/function_with_deployment_preference_condition_with_passthrough.json @@ -0,0 +1,208 @@ +{ + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "EnableAliasProvisionedConcurrency": { + "Default": true, + "Type": "String", + "AllowedValues": [ + true, + false + ] + }, + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Default": 10, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "FunctionCondition" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "FunctionCondition" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "FunctionCondition" + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "FunctionCondition", + "Properties": { + "Name": "live", + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + } + } + } + } +} diff --git a/tests/translator/output/function_with_deployment_preference_condition_without_passthrough.json b/tests/translator/output/function_with_deployment_preference_condition_without_passthrough.json new file mode 100644 index 000000000..643c86b29 --- /dev/null +++ b/tests/translator/output/function_with_deployment_preference_condition_without_passthrough.json @@ -0,0 +1,205 @@ +{ + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "EnableAliasProvisionedConcurrency": { + "Default": true, + "Type": "String", + "AllowedValues": [ + true, + false + ] + }, + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Default": 10, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + } + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "FunctionCondition" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "FunctionCondition" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "FunctionCondition" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "FunctionCondition", + "Properties": { + "Name": "live", + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + } + } + } + } + } \ No newline at end of file diff --git a/tests/translator/output/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json b/tests/translator/output/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json new file mode 100644 index 000000000..f908e21be --- /dev/null +++ b/tests/translator/output/function_with_deployment_preference_multiple_combinations_conditions_with_passthrough.json @@ -0,0 +1,585 @@ +{ + "Conditions": { + "ServerlessCodeDeployCondition": { + "Fn::Or": [ + { + "Condition": "Condition2" + }, + { + "Condition": "Condition1" + } + ] + }, + "Condition3": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition2": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition1": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": { + "MyFalseParameter": { + "Default": false, + "Type": "String" + } + }, + "Resources": { + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition1" + }, + "MyCloudWatchAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "EvaluationPeriods": 1, + "Namespace": "AWS/EC2", + "Period": 300, + "ComparisonOperator": "GreaterThanThreshold", + "Threshold": 10, + "MetricName": "MyMetric" + } + }, + "MinimalFunctionWithMinimalDeploymentPreference": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition1" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + } + }, + "Condition": "Condition2" + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + } + }, + "Condition": "ServerlessCodeDeployCondition" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Canary10Percent5Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsAliaslivewithdeploymentwithhooksandalarms": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "BeforeAllowTrafficHook": { + "Ref": "MySanityTestFunction" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup" + }, + "AfterAllowTrafficHook": { + "Ref": "MyValidationTestFunction" + } + } + }, + "Condition": "Condition2", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + }, + "Name": "livewithdeploymentwithhooksandalarms" + } + }, + "MyValidationTestFunctionRole": { + "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" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceRole": { + "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" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition1" + }, + "MySanityTestFunctionRole": { + "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" + } + ] + }, + "Condition": "Condition3" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "AlarmConfiguration": { + "Alarms": [ + { + "Name": { + "Ref": "MyCloudWatchAlarm" + } + } + ], + "Enabled": true + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + } + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole": { + "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" + } + ] + }, + "Condition": "Condition2" + }, + "MinimalFunctionWithMinimalDeploymentPreferenceAliaslivewithdeployment": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup" + } + } + }, + "Condition": "Condition2", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + }, + "Name": "livewithdeployment" + } + }, + "MySanityTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "mySanityTestFunction.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MySanityTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + }, + "Condition": "Condition3" + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + }, + "Condition": "ServerlessCodeDeployCondition" + }, + "MinimalFunctionVersion640128d35d": { + "DeletionPolicy": "Retain", + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + }, + "Condition": "Condition1" + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Condition": "Condition1", + "Properties": { + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + }, + "FunctionName": { + "Ref": "MinimalFunction" + }, + "Name": "live" + } + }, + "MyValidationTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "hello.handler", + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "myValidationTestFunction.zip" + }, + "Role": { + "Fn::GetAtt": [ + "MyValidationTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} diff --git a/tests/translator/output/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json b/tests/translator/output/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json new file mode 100644 index 000000000..0519f0b44 --- /dev/null +++ b/tests/translator/output/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json @@ -0,0 +1,570 @@ +{ + "Conditions": { + "Condition1": { + "Fn::Equals": [ + true, + true + ] + }, + "Condition2": { + "Fn::Equals": [ + true, + false + ] + }, + "Condition3": { + "Fn::Equals": [ + true, + false + ] + } + }, + "Parameters": { + "MyFalseParameter": { + "Type": "String", + "Default": false + } + }, + "Resources": { + "MyCloudWatchAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "EvaluationPeriods": 1, + "MetricName": "MyMetric", + "Namespace": "AWS/EC2", + "Period": 300, + "Threshold": 10 + } + }, + "MinimalFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition1", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition1", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunction" + } + } + }, + "MinimalFunctionAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition1", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "MinimalFunction" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition1", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreference": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition2", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition2", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceAliaslivewithdeployment": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition2", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup" + } + } + }, + "Properties": { + "Name": "livewithdeployment", + "FunctionName": { + "Ref": "MinimalFunctionWithMinimalDeploymentPreference" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithMinimalDeploymentPreferenceVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition2", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition2", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "Condition2", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsAliaslivewithdeploymentwithhooksandalarms": { + "Type": "AWS::Lambda::Alias", + "Condition": "Condition2", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup" + }, + "BeforeAllowTrafficHook": { + "Ref": "MySanityTestFunction" + }, + "AfterAllowTrafficHook": { + "Ref": "MyValidationTestFunction" + } + } + }, + "Properties": { + "Name": "livewithdeploymentwithhooksandalarms", + "FunctionName": { + "Ref": "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarms" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsVersion640128d35d", + "Version" + ] + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition2", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MySanityTestFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "Condition3", + "Properties": { + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "mySanityTestFunction.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MySanityTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MySanityTestFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "Condition3", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyValidationTestFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "my-bucket", + "S3Key": "myValidationTestFunction.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "MyValidationTestFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyValidationTestFunctionRole": { + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ] + } + }, + "MinimalFunctionDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "MinimalFunctionWithMinimalDeploymentPreferenceDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Canary10Percent5Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "MinimalFunctionWithDeploymentPreferenceWithHooksAndAlarmsDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "AlarmConfiguration": { + "Enabled": true, + "Alarms": [ + { + "Name": { + "Ref": "MyCloudWatchAlarm" + } + } + ] + }, + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery2Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + } + } +} diff --git a/tests/translator/output/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json b/tests/translator/output/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json new file mode 100644 index 000000000..9efc3b9c1 --- /dev/null +++ b/tests/translator/output/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json @@ -0,0 +1,649 @@ +{ + "Mappings": { + "HelloWorldMap": { + "hello": { + "key1": true, + "key2": false + }, + "world": { + "key1": false, + "key2": true + } + } + }, + "Parameters": { + "FnName": { + "Type": "String" + }, + "ProvisionedConcurrency": { + "Type": "String", + "Default": 10 + }, + "EnableAliasProvisionedConcurrency": { + "Type": "String", + "AllowedValues": [ + true, + false + ], + "Default": true + }, + "DefaultTrueParam": { + "Type": "String", + "Default": "true" + }, + "DefaultFalseParam": { + "Type": "String", + "Default": "false" + }, + "HelloParam": { + "Type": "String", + "Default": "hello" + }, + "WorldParam": { + "Type": "String", + "Default": "world" + } + }, + "Conditions": { + "AliasProvisionedConcurrencyEnabled": { + "Fn::Equals": [ + { + "Ref": "EnableAliasProvisionedConcurrency" + }, + true + ] + }, + "FunctionCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Resources": { + "TrueRef": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "TrueRefRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueRefVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "TrueRef" + } + } + }, + "TrueRefAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "TrueRefDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "TrueRef" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "TrueRefVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "TrueRefRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseRef": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FalseRefRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseRefVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "FalseRef" + } + } + }, + "FalseRefAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "FalseRefDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "FalseRef" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "FalseRefVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "FalseRefRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueFindInMap": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "TrueFindInMapRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "TrueFindInMapVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "TrueFindInMap" + } + } + }, + "TrueFindInMapAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "TrueFindInMapDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "TrueFindInMap" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "TrueFindInMapVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "TrueFindInMapRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseFindInMap": { + "Type": "AWS::Lambda::Function", + "Condition": "FunctionCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "FalseFindInMapRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "FalseFindInMapVersion640128d35d": { + "Type": "AWS::Lambda::Version", + "Condition": "FunctionCondition", + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "FalseFindInMap" + } + } + }, + "FalseFindInMapAliaslive": { + "Type": "AWS::Lambda::Alias", + "Condition": "FunctionCondition", + "UpdatePolicy": { + "CodeDeployLambdaAliasUpdate": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "DeploymentGroupName": { + "Ref": "FalseFindInMapDeploymentGroup" + } + } + }, + "Properties": { + "Name": "live", + "FunctionName": { + "Ref": "FalseFindInMap" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "FalseFindInMapVersion640128d35d", + "Version" + ] + }, + "ProvisionedConcurrencyConfig": { + "Fn::If": [ + "AliasProvisionedConcurrencyEnabled", + { + "ProvisionedConcurrentExecutions": { + "Ref": "ProvisionedConcurrency" + } + }, + { + "Ref": "AWS::NoValue" + } + ] + } + } + }, + "FalseFindInMapRole": { + "Type": "AWS::IAM::Role", + "Condition": "FunctionCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ServerlessDeploymentApplication": { + "Type": "AWS::CodeDeploy::Application", + "Properties": { + "ComputePlatform": "Lambda" + } + }, + "CodeDeployServiceRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "codedeploy.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda" + ] + } + }, + "TrueRefDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Condition": "FunctionCondition", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "FalseRefDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "TrueFindInMapDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Condition": "FunctionCondition", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + }, + "FalseFindInMapDeploymentGroup": { + "Type": "AWS::CodeDeploy::DeploymentGroup", + "Properties": { + "ApplicationName": { + "Ref": "ServerlessDeploymentApplication" + }, + "AutoRollbackConfiguration": { + "Enabled": true, + "Events": [ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + "DEPLOYMENT_STOP_ON_REQUEST" + ] + }, + "DeploymentConfigName": { + "Fn::Sub": [ + "CodeDeployDefault.Lambda${ConfigName}", + { + "ConfigName": "Linear10PercentEvery3Minutes" + } + ] + }, + "DeploymentStyle": { + "DeploymentType": "BLUE_GREEN", + "DeploymentOption": "WITH_TRAFFIC_CONTROL" + }, + "ServiceRoleArn": { + "Fn::GetAtt": [ + "CodeDeployServiceRole", + "Arn" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/function_with_function_url_config_and_autopublishalias.json b/tests/translator/output/function_with_function_url_config_and_autopublishalias.json index 7c547aba9..82d8dd8e1 100644 --- a/tests/translator/output/function_with_function_url_config_and_autopublishalias.json +++ b/tests/translator/output/function_with_function_url_config_and_autopublishalias.json @@ -85,7 +85,7 @@ "Properties": { "Action": "lambda:InvokeFunctionUrl", "FunctionName": { - "Ref": "MyFunction" + "Ref": "MyFunctionAliaslive" }, "Principal": "*", "FunctionUrlAuthType": "NONE" diff --git a/tests/translator/output/function_with_function_url_config_conditions.json b/tests/translator/output/function_with_function_url_config_conditions.json new file mode 100644 index 000000000..9fa4a6b0b --- /dev/null +++ b/tests/translator/output/function_with_function_url_config_conditions.json @@ -0,0 +1,113 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Conditions": { + "MyCondition": { + "Fn::Equals": [ + true, + true + ] + } + }, + "Parameters": {}, + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Condition": "MyCondition", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Description": "Created by SAM", + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Timeout": 3, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "MyFunctionUrl": { + "Type": "AWS::Lambda::Url", + "Condition": "MyCondition", + "Properties": { + "TargetFunctionArn": { + "Ref": "MyFunction" + }, + "AuthType": "NONE", + "Cors": { + "AllowOrigins": [ + "https://example.com", + "example1.com", + "example2.com", + "example2.com" + ], + "AllowMethods": [ + "GET" + ], + "AllowCredentials": true, + "AllowHeaders": [ + "x-Custom-Header" + ], + "ExposeHeaders": [ + "x-amzn-header" + ], + "MaxAge": 10 + } + } + }, + "MyFunctionUrlPublicPermissions": { + "Type": "AWS::Lambda::Permission", + "Condition": "MyCondition", + "Properties": { + "Action": "lambda:InvokeFunctionUrl", + "FunctionName": { + "Ref": "MyFunction" + }, + "Principal": "*", + "FunctionUrlAuthType": "NONE" + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Condition": "MyCondition", + "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": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/http_api_custom_iam_auth.json b/tests/translator/output/http_api_custom_iam_auth.json new file mode 100644 index 000000000..d4fb4acef --- /dev/null +++ b/tests/translator/output/http_api_custom_iam_auth.json @@ -0,0 +1,137 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionHelloAWSIAMAuthPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "MyApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/hello-aws-iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/hello-aws-iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { "name": "Tag1", "x-amazon-apigateway-tag-value": "value1" }, + { "name": "Tag2", "x-amazon-apigateway-tag-value": "value2" }, + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { + "Tag1": "value1", + "Tag2": "value2", + "httpapi:createdBy": "SAM" + } + }, + "Type": "AWS::ApiGatewayV2::Stage" + }, + "MyAuthFn": { + "Properties": { + "Code": { "S3Bucket": "bucket", "S3Key": "key" }, + "Handler": "index.handler", + "Role": { "Fn::GetAtt": ["Arn", "MyAuthFnRole"] }, + "Runtime": "nodejs12.x", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "MyAuthFnRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/http_api_global_iam_auth_enabled.json b/tests/translator/output/http_api_global_iam_auth_enabled.json new file mode 100644 index 000000000..76c2cbe95 --- /dev/null +++ b/tests/translator/output/http_api_global_iam_auth_enabled.json @@ -0,0 +1,124 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionImplicitApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "ServerlessHttpApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionImplicitApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { "__ApiId__": { "Ref": "ServerlessHttpApi" }, "__Stage__": "*" }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "ServerlessHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "ServerlessHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "ServerlessHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json b/tests/translator/output/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json new file mode 100644 index 000000000..ceea480e8 --- /dev/null +++ b/tests/translator/output/http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer.json @@ -0,0 +1,44 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": {}, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/http_api_local_iam_auth_enabled.json b/tests/translator/output/http_api_local_iam_auth_enabled.json new file mode 100644 index 000000000..e79a9585e --- /dev/null +++ b/tests/translator/output/http_api_local_iam_auth_enabled.json @@ -0,0 +1,259 @@ +{ + "Resources": { + "HttpApiFunction": { + "Properties": { + "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "todo_list.zip" }, + "Handler": "index.restapi", + "Role": { "Fn::GetAtt": ["Arn", "HttpApiFunctionRole"] }, + "Runtime": "python3.7", + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::Lambda::Function" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyDefaultIamAuthHttpApiNoAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyDefaultIamAuthHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/no-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyIamAuthEnabledHttpApiDefaultAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyIamAuthEnabledHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/default-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionMyIamAuthEnabledHttpApiIamAuthEventPermission": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "HttpApiFunction" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + { + "__ApiId__": { "Ref": "MyIamAuthEnabledHttpApi" }, + "__Stage__": "*" + }, + "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/iam-auth" + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "HttpApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": { "Service": ["lambda.amazonaws.com"] } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [{ "Key": "lambda:createdBy", "Value": "SAM" }] + }, + "Type": "AWS::IAM::Role" + }, + "MyDefaultIamAuthHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/no-auth": { + "get": { + "responses": {}, + "security": [{ "NONE": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyDefaultIamAuthHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyDefaultIamAuthHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + }, + "MyIamAuthEnabledHttpApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "in": "header", + "name": "Authorization", + "type": "apiKey", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": { + "/default-auth": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + }, + "/iam-auth": { + "get": { + "responses": {}, + "security": [{ "AWS_IAM": [] }], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + } + } + } + }, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyIamAuthEnabledHttpApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyIamAuthEnabledHttpApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json b/tests/translator/output/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json new file mode 100644 index 000000000..ceea480e8 --- /dev/null +++ b/tests/translator/output/http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer.json @@ -0,0 +1,44 @@ +{ + "Resources": { + "MyApi": { + "Properties": { + "Body": { + "components": { + "securitySchemes": { + "AWS_IAM": { + "type": "oauth2", + "x-amazon-apigateway-authorizer": { + "identitySource": "$request.header.Authorization", + "jwtConfiguration": { + "audience": ["audience1", "audience2"], + "issuer": "https://www.example.com/v1/connect/oidc" + }, + "type": "jwt" + } + } + } + }, + "info": { "title": { "Ref": "AWS::StackName" }, "version": "1.0" }, + "openapi": "3.0.1", + "paths": {}, + "tags": [ + { + "name": "httpapi:createdBy", + "x-amazon-apigateway-tag-value": "SAM" + } + ] + } + }, + "Type": "AWS::ApiGatewayV2::Api" + }, + "MyApiApiGatewayDefaultStage": { + "Properties": { + "ApiId": { "Ref": "MyApi" }, + "AutoDeploy": true, + "StageName": "$default", + "Tags": { "httpapi:createdBy": "SAM" } + }, + "Type": "AWS::ApiGatewayV2::Stage" + } + } +} diff --git a/tests/translator/output/http_api_multiple_authorizers.json b/tests/translator/output/http_api_multiple_authorizers.json index 39eee42e4..a18e784e7 100644 --- a/tests/translator/output/http_api_multiple_authorizers.json +++ b/tests/translator/output/http_api_multiple_authorizers.json @@ -9,10 +9,7 @@ }, "Handler": "index.restapi", "Role": { - "Fn::GetAtt": [ - "HttpApiFunctionRole", - "Arn" - ] + "Fn::GetAtt": ["HttpApiFunctionRole", "Arn"] }, "Runtime": "python3.7", "Tags": [ @@ -30,14 +27,10 @@ "Version": "2012-10-17", "Statement": [ { - "Action": [ - "sts:AssumeRole" - ], + "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] + "Service": ["lambda.amazonaws.com"] } } ] @@ -83,10 +76,7 @@ }, "Handler": "index.handler", "Role": { - "Fn::GetAtt": [ - "MyAuthFnRole", - "Arn" - ] + "Fn::GetAtt": ["MyAuthFnRole", "Arn"] }, "Runtime": "nodejs12.x", "Tags": [ @@ -104,14 +94,10 @@ "Version": "2012-10-17", "Statement": [ { - "Action": [ - "sts:AssumeRole" - ], + "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] + "Service": ["lambda.amazonaws.com"] } } ] @@ -168,9 +154,7 @@ "responses": {}, "security": [ { - "MyOauth2Authorizer": [ - "scope" - ] + "MyOauth2Authorizer": ["scope"] } ] }, @@ -191,6 +175,24 @@ ] } }, + "/hello-aws-iam-auth": { + "get": { + "x-amazon-apigateway-integration": { + "type": "aws_proxy", + "httpMethod": "POST", + "payloadFormatVersion": "2.0", + "uri": { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HttpApiFunction.Arn}/invocations" + } + }, + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ] + } + }, "$default": { "x-amazon-apigateway-any-method": { "x-amazon-apigateway-integration": { @@ -213,6 +215,12 @@ }, "components": { "securitySchemes": { + "AWS_IAM": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4" + }, "LambdaAuth": { "type": "apiKey", "name": "Unused", @@ -224,10 +232,7 @@ "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", { "__FunctionArn__": { - "Fn::GetAtt": [ - "MyAuthFn", - "Arn" - ] + "Fn::GetAtt": ["MyAuthFn", "Arn"] } } ] @@ -239,10 +244,7 @@ "type": "oauth2", "x-amazon-apigateway-authorizer": { "jwtConfiguration": { - "audience": [ - "audience1", - "audience2" - ], + "audience": ["audience1", "audience2"], "issuer": "https://www.example.com/v1/connect/oidc" }, "identitySource": "$request.header.Authorization", @@ -284,4 +286,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/translator/test_function_resources.py b/tests/translator/test_function_resources.py index 279668155..bb3c8e5cc 100644 --- a/tests/translator/test_function_resources.py +++ b/tests/translator/test_function_resources.py @@ -1,5 +1,7 @@ from unittest import TestCase from unittest.mock import patch, Mock +from parameterized import parameterized + import os from samtranslator.model.sam_resources import SamFunction from samtranslator.model.lambda_ import LambdaAlias, LambdaVersion, LambdaFunction @@ -144,7 +146,9 @@ def test_sam_function_with_deployment_preference(self, get_resolved_alias_name_m resources = sam_func.to_cloudformation(**kwargs) deployment_preference_collection.update_policy.assert_called_once_with(self.sam_func.logical_id) - deployment_preference_collection.add.assert_called_once_with(self.sam_func.logical_id, deploy_preference_dict) + deployment_preference_collection.add.assert_called_once_with( + self.sam_func.logical_id, deploy_preference_dict, None + ) aliases = [r.to_dict() for r in resources if r.resource_type == LambdaAlias.resource_type] @@ -222,7 +226,7 @@ def test_sam_function_with_disabled_deployment_preference_does_not_add_update_po resources = sam_func.to_cloudformation(**kwargs) - preference_collection.add.assert_called_once_with(sam_func.logical_id, deploy_preference_dict) + preference_collection.add.assert_called_once_with(sam_func.logical_id, deploy_preference_dict, None) preference_collection.get.assert_called_once_with(sam_func.logical_id) self.intrinsics_resolver_mock.resolve_parameter_refs.assert_called_with(enabled) aliases = [r.to_dict() for r in resources if r.resource_type == LambdaAlias.resource_type] @@ -318,7 +322,9 @@ def test_sam_function_with_deployment_preference_intrinsic_ref_enabled_boolean_p resources = sam.to_cloudformation(**kwargs) deployment_preference_collection.update_policy.assert_called_once_with(self.sam_func.logical_id) - deployment_preference_collection.add.assert_called_once_with(self.sam_func.logical_id, deploy_preference_dict) + deployment_preference_collection.add.assert_called_once_with( + self.sam_func.logical_id, deploy_preference_dict, None + ) self.intrinsics_resolver_mock.resolve_parameter_refs.assert_any_call(enabled) aliases = [r.to_dict() for r in resources if r.resource_type == LambdaAlias.resource_type] @@ -395,6 +401,161 @@ def test_sam_function_with_deployment_preference_intrinsic_findinmap_enabled_dic sam_func.to_cloudformation(**kwargs) self.assertTrue(sam_func.DeploymentPreference["Enabled"]) + @patch("boto3.session.Session.region_name", "ap-southeast-1") + @patch.object(SamFunction, "_get_resolved_alias_name") + def test_sam_function_with_deployment_preference_passthrough_condition_through_property( + self, get_resolved_alias_name_mock + ): + deploy_preference_dict = {"Type": "LINEAR", "PassthroughCondition": True} + alias_name = "AliasName" + func = { + "Type": "AWS::Serverless::Function", + "Condition": "Condition1", + "Properties": { + "CodeUri": self.code_uri, + "Runtime": "nodejs12.x", + "Handler": "index.handler", + "AutoPublishAlias": alias_name, + "DeploymentPreference": deploy_preference_dict, + }, + } + + sam_func = SamFunction.from_dict(logical_id="foo", resource_dict=func) + + kwargs = dict() + kwargs["managed_policy_map"] = {"a": "b"} + kwargs["event_resources"] = [] + kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + kwargs["mappings_resolver"] = self.mappings_resolver_mock + deployment_preference_collection = self._make_deployment_preference_collection() + kwargs["deployment_preference_collection"] = deployment_preference_collection + get_resolved_alias_name_mock.return_value = alias_name + + self.intrinsics_resolver_mock.resolve_parameter_refs.return_value = { + "S3Bucket": "bucket", + "S3Key": "key", + "S3ObjectVersion": "version", + } + self.mappings_resolver_mock.resolve_parameter_refs.return_value = True + resources = sam_func.to_cloudformation(**kwargs) + + deployment_preference_collection.update_policy.assert_called_once_with(self.sam_func.logical_id) + deployment_preference_collection.add.assert_called_once_with( + self.sam_func.logical_id, deploy_preference_dict, "Condition1" + ) + + aliases = [r.to_dict() for r in resources if r.resource_type == LambdaAlias.resource_type] + + self.assertTrue("UpdatePolicy" in list(aliases[0].values())[0]) + self.assertEqual(list(aliases[0].values())[0]["UpdatePolicy"], self.update_policy().to_dict()) + + @patch("boto3.session.Session.region_name", "ap-southeast-1") + @patch.object(SamFunction, "_get_resolved_alias_name") + def test_sam_function_with_deployment_preference_passthrough_condition_through_feature_flag( + self, get_resolved_alias_name_mock + ): + deploy_preference_dict = {"Type": "LINEAR"} + alias_name = "AliasName" + func = { + "Type": "AWS::Serverless::Function", + "Condition": "Condition1", + "Properties": { + "CodeUri": self.code_uri, + "Runtime": "nodejs12.x", + "Handler": "index.handler", + "AutoPublishAlias": alias_name, + "DeploymentPreference": deploy_preference_dict, + }, + } + + sam_func = SamFunction.from_dict(logical_id="foo", resource_dict=func) + + kwargs = dict() + kwargs["managed_policy_map"] = {"a": "b"} + kwargs["event_resources"] = [] + kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + kwargs["mappings_resolver"] = self.mappings_resolver_mock + deployment_preference_collection = self._make_deployment_preference_collection() + kwargs["deployment_preference_collection"] = deployment_preference_collection + get_resolved_alias_name_mock.return_value = alias_name + feature_toggle_mock = Mock() + feature_toggle_mock.is_enabled.side_effect = lambda x: x == "deployment_preference_condition_fix" + kwargs["feature_toggle"] = feature_toggle_mock + + self.intrinsics_resolver_mock.resolve_parameter_refs.return_value = { + "S3Bucket": "bucket", + "S3Key": "key", + "S3ObjectVersion": "version", + } + resources = sam_func.to_cloudformation(**kwargs) + + deployment_preference_collection.update_policy.assert_called_once_with(self.sam_func.logical_id) + deployment_preference_collection.add.assert_called_once_with( + self.sam_func.logical_id, deploy_preference_dict, "Condition1" + ) + + aliases = [r.to_dict() for r in resources if r.resource_type == LambdaAlias.resource_type] + + self.assertTrue("UpdatePolicy" in list(aliases[0].values())[0]) + self.assertEqual(list(aliases[0].values())[0]["UpdatePolicy"], self.update_policy().to_dict()) + + @parameterized.expand( + [ + ( + {"Fn::Sub": ["${Hello}", {"Hello": "helloworld"}]}, + "Resource with id [foo] is invalid. Unsupported intrinsic: the only intrinsic functions supported for property PassthroughCondition are FindInMap and parameter Refs.", + ), + ("my_string", "Resource with id [foo] is invalid. Invalid value for property PassthroughCondition."), + ] + ) + @patch("boto3.session.Session.region_name", "ap-southeast-1") + @patch.object(SamFunction, "_get_resolved_alias_name") + def test_sam_function_with_deployment_preference_passthrough_condition_invalid_input( + self, invalid_passthrough_condition, expected_exception_message, get_resolved_alias_name_mock + ): + deploy_preference_dict = {"Type": "LINEAR", "PassthroughCondition": invalid_passthrough_condition} + alias_name = "AliasName" + func = { + "Type": "AWS::Serverless::Function", + "Condition": "Condition1", + "Properties": { + "CodeUri": self.code_uri, + "Runtime": "nodejs12.x", + "Handler": "index.handler", + "AutoPublishAlias": alias_name, + "DeploymentPreference": deploy_preference_dict, + }, + } + + sam_func = SamFunction.from_dict(logical_id="foo", resource_dict=func) + + kwargs = dict() + kwargs["managed_policy_map"] = {"a": "b"} + kwargs["event_resources"] = [] + kwargs["intrinsics_resolver"] = self.intrinsics_resolver_mock + kwargs["mappings_resolver"] = self.mappings_resolver_mock + deployment_preference_collection = self._make_deployment_preference_collection() + kwargs["deployment_preference_collection"] = deployment_preference_collection + get_resolved_alias_name_mock.return_value = alias_name + feature_toggle_mock = Mock() + feature_toggle_mock.is_enabled.side_effect = lambda x: x == "deployment_preference_condition_fix" + kwargs["feature_toggle"] = feature_toggle_mock + + self.intrinsics_resolver_mock.resolve_parameter_refs.return_value = { + "S3Bucket": "bucket", + "S3Key": "key", + "S3ObjectVersion": "version", + } + self.mappings_resolver_mock.resolve_parameter_refs.return_value = invalid_passthrough_condition + + with self.assertRaises(InvalidResourceException) as e: + sam_func.to_cloudformation(**kwargs) + + self.assertEqual( + e.exception.message, + expected_exception_message, + ) + @patch("samtranslator.translator.logical_id_generator.LogicalIdGenerator") def test_version_creation(self, LogicalIdGeneratorMock): generator_mock = LogicalIdGeneratorMock.return_value diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 5f78d087a..e2942b707 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -366,14 +366,20 @@ class TestTranslatorEndToEnd(AbstractTestTranslator): "function_with_disabled_deployment_preference", "function_with_disabled_traffic_hook", "function_with_deployment_preference", + "function_with_deployment_preference_condition_with_passthrough", + "function_with_deployment_preference_condition_without_passthrough", "function_with_deployment_preference_all_parameters", "function_with_deployment_preference_from_parameters", "function_with_deployment_preference_multiple_combinations", "function_with_deployment_preference_alarms_intrinsic_if", + "function_with_deployment_preference_multiple_combinations_conditions_with_passthrough", + "function_with_deployment_preference_multiple_combinations_conditions_without_passthrough", + "function_with_deployment_preference_passthrough_condition_with_supported_intrinsics", "function_with_alias_and_event_sources", "function_with_resource_refs", "function_with_deployment_and_custom_role", - "function_with_deployment_no_service_role", + "function_with_deployment_no_service_role_with_passthrough", + "function_with_deployment_no_service_role_without_passthrough", "function_with_global_layers", "function_with_layers", "function_with_many_layers", @@ -408,6 +414,7 @@ class TestTranslatorEndToEnd(AbstractTestTranslator): "api_with_auth_and_conditions_all_max", "api_with_apikey_default_override", "api_with_apikey_required", + "api_with_apikey_source", "api_with_path_parameters", "function_with_event_source_mapping", "function_with_event_dest", @@ -472,6 +479,7 @@ class TestTranslatorEndToEnd(AbstractTestTranslator): "api_rest_paths_with_if_condition_openapi_no_value_then_case", "api_rest_paths_with_if_condition_openapi_no_value_else_case", "function_with_function_url_config", + "function_with_function_url_config_conditions", "function_with_function_url_config_with_intrinsics", "function_with_function_url_config_with_iam_authorization_type", "function_with_function_url_config_without_cors_config", @@ -530,10 +538,15 @@ def test_transform_success(self, testcase, partition_with_region): "http_api_explicit_stage", "http_api_def_uri", "explicit_http_api", + "http_api_custom_iam_auth", "http_api_with_cors", "http_api_description", + "http_api_global_iam_auth_enabled_with_existing_conflicting_authorizer", + "http_api_global_iam_auth_enabled", "http_api_lambda_auth", "http_api_lambda_auth_full", + "http_api_local_iam_auth_enabled_with_existing_conflicting_authorizer", + "http_api_local_iam_auth_enabled", "http_api_multiple_authorizers", ], [ diff --git a/tests/unit/model/preferences/test_deployment_preference_collection.py b/tests/unit/model/preferences/test_deployment_preference_collection.py index 0ab04c949..163660aed 100644 --- a/tests/unit/model/preferences/test_deployment_preference_collection.py +++ b/tests/unit/model/preferences/test_deployment_preference_collection.py @@ -20,7 +20,7 @@ def test_codedeploy_iam_role_contains_AWSCodeDeployRoleForLambdaLimited_managedp ) as get_partition_name_patch: get_partition_name_patch.return_value = partition - iam_role = DeploymentPreferenceCollection().codedeploy_iam_role + iam_role = DeploymentPreferenceCollection().get_codedeploy_iam_role() self.assertIn( "arn:{}:iam::aws:policy/service-role/AWSCodeDeployRoleForLambdaLimited".format(partition), @@ -41,7 +41,7 @@ def test_codedeploy_iam_role_contains_AWSCodeDeployRoleForLambda_managedpolicy(s ) as get_partition_name_patch: get_partition_name_patch.return_value = partition - iam_role = DeploymentPreferenceCollection().codedeploy_iam_role + iam_role = DeploymentPreferenceCollection().get_codedeploy_iam_role() self.assertIn( "arn:{}:iam::aws:policy/service-role/AWSCodeDeployRoleForLambda".format(partition), diff --git a/tests/utils/test_py27hash_fix.py b/tests/utils/test_py27hash_fix.py index 2dbf6fd37..446968359 100644 --- a/tests/utils/test_py27hash_fix.py +++ b/tests/utils/test_py27hash_fix.py @@ -99,7 +99,7 @@ def test_py27_hash(self): def test_deepcopy(self): a = Py27UniStr("abcdef") - self.assert_(a is copy.deepcopy(a)) # deepcopy should give back the same object + self.assertTrue(a is copy.deepcopy(a)) # deepcopy should give back the same object class TestPy27LongInt(TestCase): @@ -123,7 +123,7 @@ def test_serialized_dict_with_normal_int(self): def test_deepcopy(self): a = Py27LongInt(10) - self.assert_(a is copy.deepcopy(a)) # deepcopy should give back the same object + self.assertTrue(a is copy.deepcopy(a)) # deepcopy should give back the same object class TestPy27Keys(TestCase): diff --git a/tests/validator/test_validator.py b/tests/validator/test_validator.py index 6ac288d48..e35524df1 100644 --- a/tests/validator/test_validator.py +++ b/tests/validator/test_validator.py @@ -121,7 +121,7 @@ class TestValidatorTranslatorTemplates(TestValidatorBase): "function_with_alias_and_event_sources", "function_with_alias_intrinsics", "function_with_deployment_and_custom_role", - "function_with_deployment_no_service_role", + "function_with_deployment_no_service_role_with_passthrough", "function_with_deployment_preference", "function_with_deployment_preference_all_parameters", "function_with_deployment_preference_from_parameters",