From 815b6aa55ccf4e58540f307925f3486c4f758dcf Mon Sep 17 00:00:00 2001 From: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> Date: Thu, 5 Nov 2020 14:09:50 -0800 Subject: [PATCH] Fix merge conflict in release 1.29.0 (#1771) * Fix: Updated Slack Invite Link (#1712) * Updated Slack Invite Link * Restricted jsonschema to Python 2 * Forced pyrsistent to 0.16 in Python 2 * Reverted Changes to enum34 * Merge master back to develop (#1734) * Release v1.26.0 (#1680) * feat: add support for VPCEndpointIds in EndpointConfiguration * fix: update formatting with black * docs: update 2016-10-31.md * docs: added api endpointconfiguration example * docs: make example more generic * fix: remove nested EndpointConfiguration types from output * fix: only allow one EndpointConfiguration Type * doc: update example to reflect only allowing one EndpointConfiguration Type * fix : missing UserPool properties (#1506) (#1581) * fix: resource policy generation for {path+} (#1580) * refactor: Remove 2016-10-31 examples * update PR template * adjust pr template * Adding authorization scopes as list validation in ApiGatewayAuthorizer (v1 and v2). (#1670) * Adding authorization scopes as list validation in ApiGatewayAuthorizer and ApiGatewayV2Authorizer. * make black. * Adding functional test for invalid auth scope. * adding error condition for invalid test. * removing test template file. * feat: MSK event type support for AWS::Serverless::Function (#52) Co-authored-by: Steve Brown Co-authored-by: jtaylor00 Co-authored-by: Jacob Fuss Co-authored-by: Alex Wood Co-authored-by: Tarun * Update __init__.py (#1704) * Release/v1.27.0 resolveconflict (#1717) * Release v1.26.0 (#1680) * feat: add support for VPCEndpointIds in EndpointConfiguration * fix: update formatting with black * docs: update 2016-10-31.md * docs: added api endpointconfiguration example * docs: make example more generic * fix: remove nested EndpointConfiguration types from output * fix: only allow one EndpointConfiguration Type * doc: update example to reflect only allowing one EndpointConfiguration Type * fix : missing UserPool properties (#1506) (#1581) * fix: resource policy generation for {path+} (#1580) * refactor: Remove 2016-10-31 examples * update PR template * adjust pr template * Adding authorization scopes as list validation in ApiGatewayAuthorizer (v1 and v2). (#1670) * Adding authorization scopes as list validation in ApiGatewayAuthorizer and ApiGatewayV2Authorizer. * make black. * Adding functional test for invalid auth scope. * adding error condition for invalid test. * removing test template file. * feat: MSK event type support for AWS::Serverless::Function (#52) Co-authored-by: Steve Brown Co-authored-by: jtaylor00 Co-authored-by: Jacob Fuss Co-authored-by: Alex Wood Co-authored-by: Tarun * Fix: Updated Slack Invite Link (#1712) * Updated Slack Invite Link * Restricted jsonschema to Python 2 * Forced pyrsistent to 0.16 in Python 2 * Reverted Changes to enum34 Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> Co-authored-by: Steve Brown Co-authored-by: jtaylor00 Co-authored-by: Jacob Fuss Co-authored-by: Alex Wood Co-authored-by: Tarun Co-authored-by: Cosh_ Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> Co-authored-by: Steve Brown Co-authored-by: jtaylor00 Co-authored-by: Jacob Fuss Co-authored-by: Alex Wood Co-authored-by: Tarun Co-authored-by: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Co-authored-by: Cosh_ * Lambdaauth (#1733) * Add support for Lambda Authorizers in HttpAPI * Address comments and fix formatting * fix version * Validate input parameters. Update tests * black reformat Co-authored-by: Raymond Wang <14915548+wchengru@users.noreply.github.com> * Feature toggle (#1737) * Adding logic to pipe app config providers. Unit test pending * Adding some documentation to config providers. * Adding some unit tests and making black ignore json files. * minor cleanup. * Addressing PR comments. * feature: Support MTLS auth properties in REST and HTTP API domain names (#1725) * feature: Support MTLS auth properties in REST and HTTP API domain names * fix unicode != str issue in py2.7 * add SecurityPolicy because default RESTAPI is using TLS1.0 * Add new property DisableExecuteApiEndpoint * black reformat * Address comments * Add tests on invalid templates * address test failures in py2.7 * restart travis tests * fix: adding support for passing target id to EventBridgeRule (#1747) * Manage black version using requirement file (#1748) * chore: Manage black version in dev.txt - config pre-commit - config development guide - config travis Refer to the commit below in aws-sam-cli https://github.com/aws/aws-sam-cli/commit/d725db5fbfc698a9f0c7582 * Format using black 20.8b1 * Opt-out black fron dev.txt for Python 2 * Release/v1.29.0 (#1769) * Mq event source (#60) * Support AmazonMQ as event source * Set black hook language version to python3 * chore: version bump (#64) * Black reformat Co-authored-by: Kaidi He <73141777+kaidih@users.noreply.github.com> Co-authored-by: Cosh_ Co-authored-by: Raymond Wang <14915548+wchengru@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> Co-authored-by: Steve Brown Co-authored-by: jtaylor00 Co-authored-by: Jacob Fuss Co-authored-by: Alex Wood Co-authored-by: Tarun Co-authored-by: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Co-authored-by: Tolledo Co-authored-by: _sam <3804518+aahung@users.noreply.github.com> Co-authored-by: Kaidi He <73141777+kaidih@users.noreply.github.com> --- .pre-commit-config.yaml | 14 +- .travis.yml | 6 - DEVELOPMENT_GUIDE.rst | 40 +++++- Makefile | 7 +- bin/sam-translate.py | 8 +- docs/cloudformation_compatibility.rst | 10 ++ docs/internals/generated_resources.rst | 30 +++++ requirements/dev.txt | 3 + samtranslator/__init__.py | 2 +- samtranslator/feature_toggle/__init__.py | 0 .../feature_toggle/feature_toggle.py | 125 ++++++++++++++++++ samtranslator/model/__init__.py | 3 +- samtranslator/model/api/api_generator.py | 31 +++++ samtranslator/model/api/http_api_generator.py | 41 +++++- samtranslator/model/apigateway.py | 2 + samtranslator/model/apigatewayv2.py | 2 + samtranslator/model/eventsources/pull.py | 23 +++- samtranslator/model/eventsources/push.py | 4 +- samtranslator/model/iam.py | 12 +- samtranslator/model/lambda_.py | 5 +- samtranslator/model/sam_resources.py | 44 +++--- samtranslator/parser/parser.py | 2 +- .../application/serverless_app_plugin.py | 8 +- samtranslator/swagger/swagger.py | 8 +- samtranslator/translator/transform.py | 4 +- samtranslator/translator/translator.py | 13 +- .../validator/sam_schema/schema.json | 23 ++++ tests/feature_toggle/__init__.py | 0 .../input/feature_toggle_config.json | 15 +++ tests/feature_toggle/test_feature_toggle.py | 102 ++++++++++++++ .../test_cloudwatch_event_source.py | 24 ++++ tests/model/test_api_v2.py | 22 ++- tests/swagger/test_swagger.py | 7 +- tests/test_model.py | 3 +- tests/translator/input/amq.yaml | 17 +++ .../input/api_with_basic_custom_domain.yaml | 5 + .../api_with_basic_custom_domain_http.yaml | 5 + ...i_with_basic_custom_domain_intrinsics.yaml | 12 ++ ...h_basic_custom_domain_intrinsics_http.yaml | 12 ++ ..._api_mtls_configuration_invalid_field.yaml | 16 +++ ...r_api_mtls_configuration_invalid_type.yaml | 16 +++ ...papi_mtls_configuration_invalid_field.yaml | 17 +++ ...tpapi_mtls_configuration_invalid_type.yaml | 17 +++ .../input/error_missing_broker.yaml | 16 +++ tests/translator/output/amq.json | 70 ++++++++++ .../output/api_with_basic_custom_domain.json | 15 ++- .../api_with_basic_custom_domain_http.json | 10 +- ...i_with_basic_custom_domain_intrinsics.json | 23 +++- ...h_basic_custom_domain_intrinsics_http.json | 17 ++- tests/translator/output/aws-cn/amq.json | 70 ++++++++++ .../aws-cn/api_with_basic_custom_domain.json | 15 ++- .../api_with_basic_custom_domain_http.json | 10 +- ...i_with_basic_custom_domain_intrinsics.json | 21 ++- ...h_basic_custom_domain_intrinsics_http.json | 17 ++- tests/translator/output/aws-us-gov/amq.json | 70 ++++++++++ .../api_with_basic_custom_domain.json | 15 ++- .../api_with_basic_custom_domain_http.json | 12 +- ...i_with_basic_custom_domain_intrinsics.json | 21 ++- ...h_basic_custom_domain_intrinsics_http.json | 17 ++- ..._api_mtls_configuration_invalid_field.json | 8 ++ ...r_api_mtls_configuration_invalid_type.json | 8 ++ ...papi_mtls_configuration_invalid_field.json | 8 ++ ...tpapi_mtls_configuration_invalid_type.json | 8 ++ .../output/error_missing_broker.json | 6 + .../output/error_missing_queue.json | 4 +- .../output/error_missing_stream.json | 4 +- tests/translator/test_function_resources.py | 12 +- tests/translator/test_translator.py | 6 + tox.ini | 2 + 69 files changed, 1120 insertions(+), 125 deletions(-) create mode 100644 samtranslator/feature_toggle/__init__.py create mode 100644 samtranslator/feature_toggle/feature_toggle.py create mode 100644 tests/feature_toggle/__init__.py create mode 100644 tests/feature_toggle/input/feature_toggle_config.json create mode 100644 tests/feature_toggle/test_feature_toggle.py create mode 100644 tests/model/eventsources/test_cloudwatch_event_source.py create mode 100644 tests/translator/input/amq.yaml create mode 100644 tests/translator/input/error_api_mtls_configuration_invalid_field.yaml create mode 100644 tests/translator/input/error_api_mtls_configuration_invalid_type.yaml create mode 100644 tests/translator/input/error_httpapi_mtls_configuration_invalid_field.yaml create mode 100644 tests/translator/input/error_httpapi_mtls_configuration_invalid_type.yaml create mode 100644 tests/translator/input/error_missing_broker.yaml create mode 100644 tests/translator/output/amq.json create mode 100644 tests/translator/output/aws-cn/amq.json create mode 100644 tests/translator/output/aws-us-gov/amq.json create mode 100644 tests/translator/output/error_api_mtls_configuration_invalid_field.json create mode 100644 tests/translator/output/error_api_mtls_configuration_invalid_type.json create mode 100644 tests/translator/output/error_httpapi_mtls_configuration_invalid_field.json create mode 100644 tests/translator/output/error_httpapi_mtls_configuration_invalid_type.json create mode 100644 tests/translator/output/error_missing_broker.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bfef1fe87..67795f4e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,9 @@ +# https://pre-commit.com/#repository-local-hooks repos: - - repo: https://github.com/python/black - rev: 19.3b0 - hooks: - - id: black - language_version: python3.7 - exclude_types: ['markdown', 'ini', 'toml', 'rst'] +- repo: local + hooks: + - id: black + name: black + entry: black + language: system + types: [python] diff --git a/.travis.yml b/.travis.yml index 6e1f66fb7..4e4b1c008 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,12 +21,6 @@ matrix: install: # Install the code requirements -- mkdir $HOME/bin-black -- wget -O $HOME/bin-black/black https://github.com/python/black/releases/download/19.10b0/black -- chmod +x $HOME/bin-black/black -- export PATH=$PATH:$HOME/bin-black -- black --version - - make init # Install Docs requirements diff --git a/DEVELOPMENT_GUIDE.rst b/DEVELOPMENT_GUIDE.rst index ccd3f4106..26961079a 100755 --- a/DEVELOPMENT_GUIDE.rst +++ b/DEVELOPMENT_GUIDE.rst @@ -28,16 +28,44 @@ Setup Python locally using `pyenv`_ 2. Install Additional Tooling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. Black -~~~~~~~~ +Black +''''' We format our code using `Black`_ and verify the source code is black compliant -in Appveyor during PRs. You can find installation instructions on `Black's docs`_. +in Appveyor during PRs. Black will be installed automatically with ``make init``. -After installing, you can check your formatting through our Makefile by running `make black-check`. To automatically update your code to match our formatting, please run `make black`. You can also integrate Black directly in your favorite IDE (instructions -can be found `here`_) +After installing, you can run our formatting through our Makefile by +``make black`` or integrating Black directly in your favorite IDE +(instructions can be found `here `__) + +(workaround) Integrating Black directly in your favorite IDE +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Since black is installed in virtualenv, when you follow `this +instruction `__, +``which black`` might give you this + +:: + + (samtranslator37) $ where black + /Users//.pyenv/shims/black + +However, IDEs such as PyCharm (using FileWatcher) will have a hard time +invoking ``/Users//.pyenv/shims/black`` and this will happen: + +:: + + pyenv: black: command not found + + The `black' command exists in these Python versions: + 3.7.2/envs/samtranslator37 + samtranslator37 + +A simple workaround is to use +``/Users//.pyenv/versions/samtranslator37/bin/black`` instead of +``/Users//.pyenv/shims/black``. Pre-commit -~~~~~~~~~~ +'''''''''' If you don't wish to manually run black on each pr or install black manually, we have integrated black into git hooks through `pre-commit`_. After installing pre-commit, run `pre-commit install` in the root of the project. This will install black for you and run the black formatting on commit. diff --git a/Makefile b/Makefile index 709820279..c2b980b8f 100755 --- a/Makefile +++ b/Makefile @@ -9,10 +9,10 @@ test: pytest --cov samtranslator --cov-report term-missing --cov-fail-under 95 tests black: - black setup.py samtranslator/* tests/* bin/* + black setup.py samtranslator/* tests/* bin/*.py black-check: - black --check setup.py samtranslator/* tests/* bin/* + black --check setup.py samtranslator/* tests/* bin/*.py # Command to run everytime you make changes to verify everything works dev: test @@ -20,6 +20,9 @@ dev: test # Verifications to run before sending a pull request pr: black-check init dev +# Verifications to run before sending a pull request, skipping black check because black requires Python 3.6+ +pr2.7: init dev + define HELP_MESSAGE Usage: $ make [TARGETS] diff --git a/bin/sam-translate.py b/bin/sam-translate.py index bffb9fe84..8bf75ba13 100755 --- a/bin/sam-translate.py +++ b/bin/sam-translate.py @@ -35,6 +35,7 @@ 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__) @@ -95,7 +96,12 @@ def transform_template(input_file_path, output_file_path): sam_template = yaml_parse(f) try: - cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client)) + feature_toggle = FeatureToggle( + FeatureToggleLocalConfigProvider( + os.path.join(my_path, "..", "tests", "feature_toggle", "input", "feature_toggle_config.json") + ) + ) + cloud_formation_template = transform(sam_template, {}, ManagedPolicyLoader(iam_client), feature_toggle) cloud_formation_template_prettified = json.dumps(cloud_formation_template, indent=2) with open(output_file_path, "w") as f: diff --git a/docs/cloudformation_compatibility.rst b/docs/cloudformation_compatibility.rst index 50b973612..5cac0920a 100644 --- a/docs/cloudformation_compatibility.rst +++ b/docs/cloudformation_compatibility.rst @@ -108,6 +108,16 @@ StartingPosition All BatchSize All ======================== ================================== ======================== +MQ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +======================== ================================== ======================== + Property Name Intrinsic(s) Supported Reasons +======================== ================================== ======================== +Broker All +Queues All +SourceAccessConfigurations All +======================== ================================== ======================== + MSK ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ======================== ================================== ======================== diff --git a/docs/internals/generated_resources.rst b/docs/internals/generated_resources.rst index 760d52a15..3d03cd148 100644 --- a/docs/internals/generated_resources.rst +++ b/docs/internals/generated_resources.rst @@ -326,6 +326,36 @@ AWS::Lambda::Permission MyFunction\ **MyTrigger**\ Permission AWS::Lambda::EventSourceMapping MyFunction\ **MyTrigger** ================================== ================================ +MQ +^^^^^^^ + +Example: + +.. code:: yaml + + MyFunction: + Type: AWS::Serverless::Function + Properties: + ... + Events: + MyTrigger: + Type: MQ + Properties: + Broker: arn:aws:mq:us-east-2:123456789012:broker:MyBroker:b-1234a5b6-78cd-901e-2fgh-3i45j6k178l9 + SourceAccessConfigurations: + Type: BASIC_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + ... + +Additional generated resources: + +================================== ================================ +CloudFormation Resource Type Logical ID +================================== ================================ +AWS::Lambda::Permission MyFunction\ **MyTrigger**\ Permission +AWS::Lambda::EventSourceMapping MyFunction\ **MyTrigger** +================================== ================================ + MSK ^^^^^^^ diff --git a/requirements/dev.txt b/requirements/dev.txt index 35279a22a..a7b423d82 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -15,3 +15,6 @@ requests>=2.20.0 # CLI requirements docopt>=0.6.2 + +# formatter +black==20.8b1; python_version >= '3.6' diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index 6e63f1000..c347ac29b 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.28.1" +__version__ = "1.29.0" diff --git a/samtranslator/feature_toggle/__init__.py b/samtranslator/feature_toggle/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/samtranslator/feature_toggle/feature_toggle.py b/samtranslator/feature_toggle/feature_toggle.py new file mode 100644 index 000000000..510966856 --- /dev/null +++ b/samtranslator/feature_toggle/feature_toggle.py @@ -0,0 +1,125 @@ +import os +import sys +import json +import boto3 +import logging + +my_path = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, my_path + "/..") + +LOG = logging.getLogger(__name__) + + +class FeatureToggle: + """ + FeatureToggle is the class which will provide methods to query and decide if a feature is enabled based on where + SAM is executing or not. + """ + + def __init__(self, config_provider): + self.feature_config = config_provider.config + + def is_enabled_for_stage_in_region(self, feature_name, stage, region="default"): + """ + To check if feature is available for a particular stage or not. + :param feature_name: name of feature + :param stage: stage where SAM is running + :param region: region in which SAM is running + :return: + """ + if feature_name not in self.feature_config: + LOG.warning("Feature '{}' not available in Feature Toggle Config.".format(feature_name)) + return False + stage_config = self.feature_config.get(feature_name, {}).get(stage, {}) + if not stage_config: + LOG.info("Stage '{}' not enabled for Feature '{}'.".format(stage, feature_name)) + return False + region_config = stage_config.get(region, {}) if region in stage_config else stage_config.get("default", {}) + is_enabled = region_config.get("enabled", False) + LOG.info("Feature '{}' is enabled: '{}'".format(feature_name, is_enabled)) + return is_enabled + + def is_enabled_for_account_in_region(self, feature_name, stage, account_id, region="default"): + """ + To check if feature is available for a particular account or not. + :param feature_name: name of feature + :param stage: stage where SAM is running + :param account_id: account_id who is executing SAM template + :param region: region in which SAM is running + :return: + """ + if feature_name not in self.feature_config: + LOG.warning("Feature '{}' not available in Feature Toggle Config.".format(feature_name)) + return False + stage_config = self.feature_config.get(feature_name, {}).get(stage, {}) + if not stage_config: + LOG.info("Stage '{}' not enabled for Feature '{}'.".format(stage, feature_name)) + return False + account_config = stage_config.get(account_id) if account_id in stage_config else stage_config.get("default", {}) + region_config = ( + account_config.get(region, {}) if region in account_config else account_config.get("default", {}) + ) + is_enabled = region_config.get("enabled", False) + LOG.info("Feature '{}' is enabled: '{}'".format(feature_name, is_enabled)) + return is_enabled + + +class FeatureToggleConfigProvider: + """Interface for all FeatureToggle config providers""" + + def __init__(self): + pass + + @property + def config(self): + raise NotImplementedError + + +class FeatureToggleDefaultConfigProvider(FeatureToggleConfigProvider): + """Default config provider, always return False for every query.""" + + def __init__(self): + FeatureToggleConfigProvider.__init__(self) + + @property + def config(self): + return {} + + +class FeatureToggleLocalConfigProvider(FeatureToggleConfigProvider): + """Feature toggle config provider which uses a local file. This is to facilitate local testing.""" + + def __init__(self, local_config_path): + FeatureToggleConfigProvider.__init__(self) + with open(local_config_path, "r") as f: + config_json = f.read() + self.feature_toggle_config = json.loads(config_json) + + @property + def config(self): + return self.feature_toggle_config + + +class FeatureToggleAppConfigConfigProvider(FeatureToggleConfigProvider): + """Feature toggle config provider which loads config from AppConfig.""" + + def __init__(self, application_id, environment_id, configuration_profile_id): + FeatureToggleConfigProvider.__init__(self) + self.app_config_client = boto3.client("appconfig") + try: + response = self.app_config_client.get_configuration( + Application=application_id, + Environment=environment_id, + Configuration=configuration_profile_id, + ClientId="FeatureToggleAppConfigConfigProvider", + ) + binary_config_string = response["Content"].read() + self.feature_toggle_config = json.loads(binary_config_string.decode("utf-8")) + except Exception as ex: + LOG.error("Failed to load config from AppConfig: {}. Using empty config.".format(ex)) + # There is chance that AppConfig is not available in a particular region. + self.feature_toggle_config = json.loads("{}") + + @property + def config(self): + return self.feature_toggle_config diff --git a/samtranslator/model/__init__.py b/samtranslator/model/__init__.py index ed8dff914..150645589 100644 --- a/samtranslator/model/__init__.py +++ b/samtranslator/model/__init__.py @@ -355,8 +355,7 @@ def to_cloudformation(self, **kwargs): class SamResourceMacro(ResourceMacro): - """ResourceMacro that specifically refers to SAM (AWS::Serverless::*) resources. - """ + """ResourceMacro that specifically refers to SAM (AWS::Serverless::*) resources.""" # SAM resources can provide a list of properties that they expose. These properties usually resolve to # CFN resources that this SAM resource generates. This is provided as a map with the following format: diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 12208209d..8e1bd7c22 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -304,6 +304,37 @@ def _construct_api_domain(self, rest_api): domain.EndpointConfiguration = {"Types": [endpoint]} + mutual_tls_auth = self.domain.get("MutualTlsAuthentication", None) + if mutual_tls_auth: + if isinstance(mutual_tls_auth, dict): + if not set(mutual_tls_auth.keys()).issubset({"TruststoreUri", "TruststoreVersion"}): + invalid_keys = list() + for key in mutual_tls_auth.keys(): + if not key in {"TruststoreUri", "TruststoreVersion"}: + invalid_keys.append(key) + invalid_keys.sort() + raise InvalidResourceException( + ",".join(invalid_keys), + "Available MutualTlsAuthentication fields are {}.".format( + ["TruststoreUri", "TruststoreVersion"] + ), + ) + domain.MutualTlsAuthentication = {} + if mutual_tls_auth.get("TruststoreUri", None): + domain.MutualTlsAuthentication["TruststoreUri"] = mutual_tls_auth["TruststoreUri"] + if mutual_tls_auth.get("TruststoreVersion", None): + domain.MutualTlsAuthentication["TruststoreVersion"] = mutual_tls_auth["TruststoreVersion"] + else: + raise InvalidResourceException( + mutual_tls_auth, + "MutualTlsAuthentication must be a map with at least one of the following fields {}.".format( + ["TruststoreUri", "TruststoreVersion"] + ), + ) + + if self.domain.get("SecurityPolicy", None): + domain.SecurityPolicy = self.domain["SecurityPolicy"] + # Create BasepathMappings if self.domain.get("BasePath") and isinstance(self.domain.get("BasePath"), string_types): basepaths = [self.domain.get("BasePath")] diff --git a/samtranslator/model/api/http_api_generator.py b/samtranslator/model/api/http_api_generator.py index 76ccecf3f..6f251fef9 100644 --- a/samtranslator/model/api/http_api_generator.py +++ b/samtranslator/model/api/http_api_generator.py @@ -48,6 +48,7 @@ def __init__( passthrough_resource_attributes=None, domain=None, fail_on_warnings=False, + disable_execute_api_endpoint=False, ): """Constructs an API Generator class that generates API Gateway resources @@ -81,6 +82,7 @@ def __init__( self.passthrough_resource_attributes = passthrough_resource_attributes self.domain = domain self.fail_on_warnings = fail_on_warnings + self.disable_execute_api_endpoint = disable_execute_api_endpoint def _construct_http_api(self): """Constructs and returns the ApiGatewayV2 HttpApi. @@ -104,6 +106,9 @@ def _construct_http_api(self): if self.fail_on_warnings: http_api.FailOnWarnings = self.fail_on_warnings + if self.disable_execute_api_endpoint: + http_api.DisableExecuteApiEndpoint = self.disable_execute_api_endpoint + if self.definition_uri: http_api.BodyS3Location = self._construct_body_s3_dict() elif self.definition_body: @@ -211,13 +216,44 @@ def _construct_api_domain(self, http_api): self.domain["EndpointConfiguration"] = "REGIONAL" elif endpoint not in ["REGIONAL"]: raise InvalidResourceException( - self.logical_id, "EndpointConfiguration for Custom Domains must be one of {}.".format(["REGIONAL"]), + self.logical_id, + "EndpointConfiguration for Custom Domains must be one of {}.".format(["REGIONAL"]), ) domain_config["EndpointType"] = endpoint domain_config["CertificateArn"] = self.domain.get("CertificateArn") + if self.domain.get("SecurityPolicy", None): + domain_config["SecurityPolicy"] = self.domain.get("SecurityPolicy") domain.DomainNameConfigurations = [domain_config] + mutual_tls_auth = self.domain.get("MutualTlsAuthentication", None) + if mutual_tls_auth: + if isinstance(mutual_tls_auth, dict): + if not set(mutual_tls_auth.keys()).issubset({"TruststoreUri", "TruststoreVersion"}): + invalid_keys = [] + for key in mutual_tls_auth.keys(): + if key not in {"TruststoreUri", "TruststoreVersion"}: + invalid_keys.append(key) + invalid_keys.sort() + raise InvalidResourceException( + ",".join(invalid_keys), + "Available MutualTlsAuthentication fields are {}.".format( + ["TruststoreUri", "TruststoreVersion"] + ), + ) + domain.MutualTlsAuthentication = {} + if mutual_tls_auth.get("TruststoreUri", None): + domain.MutualTlsAuthentication["TruststoreUri"] = mutual_tls_auth["TruststoreUri"] + if mutual_tls_auth.get("TruststoreVersion", None): + domain.MutualTlsAuthentication["TruststoreVersion"] = mutual_tls_auth["TruststoreVersion"] + else: + raise InvalidResourceException( + mutual_tls_auth, + "MutualTlsAuthentication must be a map with at least one of the following fields {}.".format( + ["TruststoreUri", "TruststoreVersion"] + ), + ) + # Create BasepathMappings if self.domain.get("BasePath") and isinstance(self.domain.get("BasePath"), string_types): basepaths = [self.domain.get("BasePath")] @@ -317,7 +353,8 @@ def _construct_alias_target(self, domain): alias_target["DNSName"] = fnGetAtt(self.domain.get("ApiDomainName"), "RegionalDomainName") else: raise InvalidResourceException( - self.logical_id, "Only REGIONAL endpoint is supported on HTTP APIs.", + self.logical_id, + "Only REGIONAL endpoint is supported on HTTP APIs.", ) return alias_target diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index 461d656e0..649f277c6 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -165,6 +165,8 @@ class ApiGatewayDomainName(Resource): "RegionalCertificateArn": PropertyType(False, is_str()), "DomainName": PropertyType(True, is_str()), "EndpointConfiguration": PropertyType(False, is_type(dict)), + "MutualTlsAuthentication": PropertyType(False, is_type(dict)), + "SecurityPolicy": PropertyType(False, is_str()), "CertificateArn": PropertyType(False, is_str()), } diff --git a/samtranslator/model/apigatewayv2.py b/samtranslator/model/apigatewayv2.py index ea4675eec..c7c1ea9be 100644 --- a/samtranslator/model/apigatewayv2.py +++ b/samtranslator/model/apigatewayv2.py @@ -14,6 +14,7 @@ class ApiGatewayV2HttpApi(Resource): "BodyS3Location": PropertyType(False, is_type(dict)), "Description": PropertyType(False, is_str()), "FailOnWarnings": PropertyType(False, is_type(bool)), + "DisableExecuteApiEndpoint": PropertyType(False, is_type(bool)), "BasePath": PropertyType(False, is_str()), "CorsConfiguration": PropertyType(False, is_type(dict)), } @@ -44,6 +45,7 @@ class ApiGatewayV2DomainName(Resource): property_types = { "DomainName": PropertyType(True, is_str()), "DomainNameConfigurations": PropertyType(False, list_of(is_type(dict))), + "MutualTlsAuthentication": PropertyType(False, is_type(dict)), "Tags": PropertyType(False, is_type(dict)), } diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index f629b56c0..7ead57569 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -10,7 +10,7 @@ class PullEventSource(ResourceMacro): """Base class for pull event sources for SAM Functions. - The pull events are Kinesis Streams, DynamoDB Streams, Kafka Streams and SQS Queues. All of these correspond to an + The pull events are Kinesis Streams, DynamoDB Streams, Kafka Topics, ActiveMQ Queues and SQS Queues. All of these correspond to an EventSourceMapping in Lambda, and require that the execution role be given to Kinesis Streams, DynamoDB Streams, or SQS Queues, respectively. @@ -31,6 +31,9 @@ class PullEventSource(ResourceMacro): "DestinationConfig": PropertyType(False, is_type(dict)), "ParallelizationFactor": PropertyType(False, is_type(int)), "Topics": PropertyType(False, is_type(list)), + "Broker": PropertyType(False, is_str()), + "Queues": PropertyType(False, is_type(list)), + "SourceAccessConfigurations": PropertyType(False, is_type(list)), } def get_policy_arn(self): @@ -60,16 +63,17 @@ def to_cloudformation(self, **kwargs): except NotImplementedError: function_name_or_arn = function.get_runtime_attr("arn") - if not self.Stream and not self.Queue: + if not self.Stream and not self.Queue and not self.Broker: raise InvalidEventException( - self.relative_id, "No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." + self.relative_id, + "No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for ActiveMQ) provided.", ) if self.Stream and not self.StartingPosition: raise InvalidEventException(self.relative_id, "StartingPosition is required for Kinesis, DynamoDB and MSK.") lambda_eventsourcemapping.FunctionName = function_name_or_arn - lambda_eventsourcemapping.EventSourceArn = self.Stream or self.Queue + lambda_eventsourcemapping.EventSourceArn = self.Stream or self.Queue or self.Broker lambda_eventsourcemapping.StartingPosition = self.StartingPosition lambda_eventsourcemapping.BatchSize = self.BatchSize lambda_eventsourcemapping.Enabled = self.Enabled @@ -79,6 +83,8 @@ def to_cloudformation(self, **kwargs): lambda_eventsourcemapping.MaximumRecordAgeInSeconds = self.MaximumRecordAgeInSeconds lambda_eventsourcemapping.ParallelizationFactor = self.ParallelizationFactor lambda_eventsourcemapping.Topics = self.Topics + lambda_eventsourcemapping.Queues = self.Queues + lambda_eventsourcemapping.SourceAccessConfigurations = self.SourceAccessConfigurations destination_config_policy = None if self.DestinationConfig: @@ -170,3 +176,12 @@ class MSK(PullEventSource): def get_policy_arn(self): return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaMSKExecutionRole") + + +class MQ(PullEventSource): + """MQ event source.""" + + resource_type = "MQ" + + def get_policy_arn(self): + return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaAMQExecutionRole") diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 03ec6a8fb..f387ab5cc 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -150,6 +150,7 @@ class CloudWatchEvent(PushEventSource): "Pattern": PropertyType(False, is_type(dict)), "Input": PropertyType(False, is_str()), "InputPath": PropertyType(False, is_str()), + "Target": PropertyType(False, is_type(dict)), } def to_cloudformation(self, **kwargs): @@ -187,7 +188,8 @@ def _construct_target(self, function): :returns: the Target property :rtype: dict """ - target = {"Arn": function.get_runtime_attr("arn"), "Id": self.logical_id + "LambdaTarget"} + target_id = self.Target["Id"] if self.Target and "Id" in self.Target else self.logical_id + "LambdaTarget" + target = {"Arn": function.get_runtime_attr("arn"), "Id": target_id} if self.Input is not None: target["Input"] = self.Input diff --git a/samtranslator/model/iam.py b/samtranslator/model/iam.py index d72f98dac..65200b182 100644 --- a/samtranslator/model/iam.py +++ b/samtranslator/model/iam.py @@ -23,7 +23,11 @@ def construct_assume_role_policy_for_service_principal(cls, service_principal): document = { "Version": "2012-10-17", "Statement": [ - {"Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": {"Service": [service_principal]},} + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": {"Service": [service_principal]}, + } ], } return document @@ -43,7 +47,11 @@ def stepfunctions_assume_role_policy(cls): document = { "Version": "2012-10-17", "Statement": [ - {"Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": {"Service": ["states.amazonaws.com"]},} + { + "Action": ["sts:AssumeRole"], + "Effect": "Allow", + "Principal": {"Service": ["states.amazonaws.com"]}, + } ], } return document diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index 5579dd0f6..0cb75661c 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -70,6 +70,8 @@ class LambdaEventSourceMapping(Resource): "ParallelizationFactor": PropertyType(False, is_type(int)), "StartingPosition": PropertyType(False, is_str()), "Topics": PropertyType(False, is_type(list)), + "Queues": PropertyType(False, is_type(list)), + "SourceAccessConfigurations": PropertyType(False, is_type(list)), } runtime_attrs = {"name": lambda self: ref(self.logical_id)} @@ -99,8 +101,7 @@ class LambdaEventInvokeConfig(Resource): class LambdaLayerVersion(Resource): - """ Lambda layer version resource - """ + """Lambda layer version resource""" resource_type = "AWS::Lambda::LayerVersion" property_types = { diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 77dcafcb3..97fdcbfc0 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -49,8 +49,7 @@ class SamFunction(SamResourceMacro): - """SAM function macro. - """ + """SAM function macro.""" resource_type = "AWS::Serverless::Function" property_types = { @@ -697,8 +696,7 @@ def _validate_deployment_preference_and_add_update_policy( class SamApi(SamResourceMacro): - """SAM rest API macro. - """ + """SAM rest API macro.""" resource_type = "AWS::Serverless::Api" property_types = { @@ -810,8 +808,7 @@ def to_cloudformation(self, **kwargs): class SamHttpApi(SamResourceMacro): - """SAM rest API macro. - """ + """SAM rest API macro.""" resource_type = "AWS::Serverless::HttpApi" property_types = { @@ -833,6 +830,7 @@ class SamHttpApi(SamResourceMacro): "RouteSettings": PropertyType(False, is_type(dict)), "Domain": PropertyType(False, is_type(dict)), "FailOnWarnings": PropertyType(False, is_type(bool)), + "DisableExecuteApiEndpoint": PropertyType(False, is_type(bool)), } referable_properties = { @@ -872,9 +870,16 @@ def to_cloudformation(self, **kwargs): passthrough_resource_attributes=self.get_passthrough_resource_attributes(), domain=self.Domain, fail_on_warnings=self.FailOnWarnings, + disable_execute_api_endpoint=self.DisableExecuteApiEndpoint, ) - (http_api, stage, domain, basepath_mapping, route53,) = api_generator.to_cloudformation() + ( + http_api, + stage, + domain, + basepath_mapping, + route53, + ) = api_generator.to_cloudformation() resources.append(http_api) if domain: @@ -892,8 +897,7 @@ def to_cloudformation(self, **kwargs): class SamSimpleTable(SamResourceMacro): - """SAM simple table macro. - """ + """SAM simple table macro.""" resource_type = "AWS::Serverless::SimpleTable" property_types = { @@ -952,8 +956,7 @@ def _convert_attribute_type(self, attribute_type): class SamApplication(SamResourceMacro): - """SAM application macro. - """ + """SAM application macro.""" APPLICATION_ID_KEY = "ApplicationId" SEMANTIC_VERSION_KEY = "SemanticVersion" @@ -971,14 +974,12 @@ class SamApplication(SamResourceMacro): } def to_cloudformation(self, **kwargs): - """Returns the stack with the proper parameters for this application - """ + """Returns the stack with the proper parameters for this application""" nested_stack = self._construct_nested_stack() return [nested_stack] def _construct_nested_stack(self): - """Constructs a AWS::CloudFormation::Stack resource - """ + """Constructs a AWS::CloudFormation::Stack resource""" nested_stack = NestedStack( self.logical_id, depends_on=self.depends_on, attributes=self.get_passthrough_resource_attributes() ) @@ -992,8 +993,7 @@ def _construct_nested_stack(self): return nested_stack def _get_application_tags(self): - """Adds tags to the stack if this resource is using the serverless app repo - """ + """Adds tags to the stack if this resource is using the serverless app repo""" application_tags = {} if isinstance(self.Location, dict): if self.APPLICATION_ID_KEY in self.Location.keys() and self.Location[self.APPLICATION_ID_KEY] is not None: @@ -1007,8 +1007,7 @@ def _get_application_tags(self): class SamLayerVersion(SamResourceMacro): - """ SAM Layer macro - """ + """SAM Layer macro""" resource_type = "AWS::Serverless::LayerVersion" property_types = { @@ -1106,8 +1105,7 @@ def _get_retention_policy_value(self): class SamStateMachine(SamResourceMacro): - """SAM state machine macro. - """ + """SAM state machine macro.""" resource_type = "AWS::Serverless::StateMachine" property_types = { @@ -1123,7 +1121,9 @@ class SamStateMachine(SamResourceMacro): "Policies": PropertyType(False, one_of(is_str(), list_of(one_of(is_str(), is_type(dict), is_type(dict))))), "Tracing": PropertyType(False, is_type(dict)), } - event_resolver = ResourceTypeResolver(samtranslator.model.stepfunctions.events,) + event_resolver = ResourceTypeResolver( + samtranslator.model.stepfunctions.events, + ) def to_cloudformation(self, **kwargs): managed_policy_map = kwargs.get("managed_policy_map", {}) diff --git a/samtranslator/parser/parser.py b/samtranslator/parser/parser.py index 6f1376620..a29999811 100644 --- a/samtranslator/parser/parser.py +++ b/samtranslator/parser/parser.py @@ -14,7 +14,7 @@ def parse(self, sam_template, parameter_values, sam_plugins): # private methods def _validate(self, sam_template, parameter_values): - """ Validates the template and parameter values and raises exceptions if there's an issue + """Validates the template and parameter values and raises exceptions if there's an issue :param dict sam_template: SAM template :param dict parameter_values: Dictionary of parameter values provided by the user diff --git a/samtranslator/plugins/application/serverless_app_plugin.py b/samtranslator/plugins/application/serverless_app_plugin.py index 2f6d42dca..c043ceeca 100644 --- a/samtranslator/plugins/application/serverless_app_plugin.py +++ b/samtranslator/plugins/application/serverless_app_plugin.py @@ -294,9 +294,11 @@ def on_after_transform_template(self, template): # Check each resource to make sure it's active for application_id, template_id in temp: - get_cfn_template = lambda application_id, template_id: self._sar_client.get_cloud_formation_template( - ApplicationId=self._sanitize_sar_str_param(application_id), - TemplateId=self._sanitize_sar_str_param(template_id), + get_cfn_template = ( + lambda application_id, template_id: self._sar_client.get_cloud_formation_template( + ApplicationId=self._sanitize_sar_str_param(application_id), + TemplateId=self._sanitize_sar_str_param(template_id), + ) ) response = self._sar_service_call(get_cfn_template, application_id, application_id, template_id) self._handle_get_cfn_template_response(response, application_id, template_id) diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index 100a8955b..ed4c72003 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -208,7 +208,13 @@ def add_lambda_integration( path_dict[method] = make_conditional(condition, path_dict[method]) def add_state_machine_integration( - self, path, method, integration_uri, credentials, request_templates=None, condition=None, + self, + path, + method, + integration_uri, + credentials, + request_templates=None, + condition=None, ): """ Adds aws APIGW integration to the given path+method. diff --git a/samtranslator/translator/transform.py b/samtranslator/translator/transform.py index be34a60b5..f3c564f39 100644 --- a/samtranslator/translator/transform.py +++ b/samtranslator/translator/transform.py @@ -2,7 +2,7 @@ from samtranslator.parser.parser import Parser -def transform(input_fragment, parameter_values, managed_policy_loader): +def transform(input_fragment, parameter_values, managed_policy_loader, feature_toggle=None): """Translates the SAM manifest provided in the and returns the translation to CloudFormation. :param dict input_fragment: the SAM template to transform @@ -13,4 +13,4 @@ def transform(input_fragment, parameter_values, managed_policy_loader): sam_parser = Parser() translator = Translator(managed_policy_loader.load(), sam_parser) - return translator.translate(input_fragment, parameter_values=parameter_values) + return translator.translate(input_fragment, parameter_values=parameter_values, feature_toggle=feature_toggle) diff --git a/samtranslator/translator/translator.py b/samtranslator/translator/translator.py index 70014a6e4..29e697489 100644 --- a/samtranslator/translator/translator.py +++ b/samtranslator/translator/translator.py @@ -1,4 +1,10 @@ import copy + +from samtranslator.feature_toggle.feature_toggle import ( + FeatureToggle, + FeatureToggleLocalConfigProvider, + FeatureToggleDefaultConfigProvider, +) from samtranslator.model import ResourceTypeResolver, sam_resources from samtranslator.translator.verify_logical_id import verify_unique_logical_id from samtranslator.model.preferences.deployment_preference_collection import DeploymentPreferenceCollection @@ -22,8 +28,7 @@ class Translator: - """Translates SAM templates into CloudFormation templates - """ + """Translates SAM templates into CloudFormation templates""" def __init__(self, managed_policy_map, sam_parser, plugins=None): """ @@ -35,6 +40,7 @@ def __init__(self, managed_policy_map, sam_parser, plugins=None): self.managed_policy_map = managed_policy_map self.plugins = plugins self.sam_parser = sam_parser + self.feature_toggle = None def _get_function_names(self, resource_dict, intrinsics_resolver): """ @@ -66,7 +72,7 @@ def _get_function_names(self, resource_dict, intrinsics_resolver): ) return self.function_names - def translate(self, sam_template, parameter_values): + def translate(self, sam_template, parameter_values, feature_toggle=None): """Loads the SAM resources from the given SAM manifest, replaces them with their corresponding CloudFormation resources, and returns the resulting CloudFormation template. @@ -81,6 +87,7 @@ def translate(self, sam_template, parameter_values): :returns: a copy of the template with SAM resources replaced with the corresponding CloudFormation, which may \ be dumped into a valid CloudFormation JSON or YAML template """ + self.feature_toggle = feature_toggle if feature_toggle else FeatureToggle(FeatureToggleDefaultConfigProvider()) self.function_names = dict() self.redeploy_restapi_parameters = dict() sam_parameter_values = SamParameterValues(parameter_values) diff --git a/samtranslator/validator/sam_schema/schema.json b/samtranslator/validator/sam_schema/schema.json index 19699acae..b0c83761c 100644 --- a/samtranslator/validator/sam_schema/schema.json +++ b/samtranslator/validator/sam_schema/schema.json @@ -477,6 +477,9 @@ { "$ref": "#/definitions/AWS::Serverless::Function.MSKEvent" }, + { + "$ref": "#/definitions/AWS::Serverless::Function.MQEvent" + }, { "$ref": "#/definitions/AWS::Serverless::Function.SQSEvent" }, @@ -609,6 +612,26 @@ ], "type": "object" }, + "AWS::Serverless::Function.MQEvent": { + "additionalProperties": false, + "properties": { + "Broker": { + "type": "string" + }, + "Queues": { + "type": "array" + }, + "SourceAccessConfigurations": { + "type": "array" + } + }, + "required": [ + "Broker", + "Queues", + "SourceAccessConfigurations" + ], + "type": "object" + }, "AWS::Serverless::Function.SQSEvent": { "additionalProperties": false, diff --git a/tests/feature_toggle/__init__.py b/tests/feature_toggle/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/feature_toggle/input/feature_toggle_config.json b/tests/feature_toggle/input/feature_toggle_config.json new file mode 100644 index 000000000..d4b8ebabf --- /dev/null +++ b/tests/feature_toggle/input/feature_toggle_config.json @@ -0,0 +1,15 @@ +{ + "__note__": "This is a dummy config for local testing. Any change here need to be migrated to SAM service.", + "feature-1": { + "beta": { + "us-west-2": {"enabled": true}, + "default": {"enabled": false}, + "123456789123": {"us-west-2": {"enabled": true}, "default": {"enabled": false}} + }, + "gamma": { + "default": {"enabled": false}, + "123456789123": {"us-east-1": {"enabled": false}, "default": {"enabled": false}} + }, + "prod": {"default": {"enabled": false}} + } +} diff --git a/tests/feature_toggle/test_feature_toggle.py b/tests/feature_toggle/test_feature_toggle.py new file mode 100644 index 000000000..c4df7d683 --- /dev/null +++ b/tests/feature_toggle/test_feature_toggle.py @@ -0,0 +1,102 @@ +from mock import patch, Mock +from parameterized import parameterized, param +from unittest import TestCase +import os, sys + +from samtranslator.feature_toggle.feature_toggle import ( + FeatureToggle, + FeatureToggleLocalConfigProvider, + FeatureToggleAppConfigConfigProvider, +) + +my_path = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, my_path + "/..") + + +class TestFeatureToggle(TestCase): + @parameterized.expand( + [ + param("feature-1", "beta", "default", False), + param("feature-1", "beta", "us-west-2", True), + param("feature-2", "beta", "us-west-2", False), # because feature is missing + ] + ) + def test_feature_toggle_with_local_provider_for_stage(self, feature_name, stage, region, expected): + feature_toggle = FeatureToggle( + FeatureToggleLocalConfigProvider(os.path.join(my_path, "input", "feature_toggle_config.json")) + ) + self.assertEqual(feature_toggle.is_enabled_for_stage_in_region(feature_name, stage, region), expected) + + @parameterized.expand( + [ + param("feature-1", "beta", "default", "123456789123", False), + param("feature-1", "beta", "us-west-2", "123456789123", True), + param("feature-2", "beta", "us-west-2", "123456789124", False), # because feature is missing + ] + ) + def test_feature_toggle_with_local_provider_for_account_id(self, feature_name, stage, region, account_id, expected): + feature_toggle = FeatureToggle( + FeatureToggleLocalConfigProvider(os.path.join(my_path, "input", "feature_toggle_config.json")) + ) + self.assertEqual( + feature_toggle.is_enabled_for_account_in_region(feature_name, stage, account_id, region), expected + ) + + +class TestFeatureToggleAppConfig(TestCase): + def setUp(self): + self.content_stream_mock = Mock() + self.content_stream_mock.read.return_value = b""" + { + "feature-1": { + "beta": { + "us-west-2": {"enabled": true}, + "default": {"enabled": false}, + "123456789123": {"us-west-2": {"enabled": true}, "default": {"enabled": false}} + }, + "gamma": { + "default": {"enabled": false}, + "123456789123": {"us-east-1": {"enabled": false}, "default": {"enabled": false}} + }, + "prod": {"default": {"enabled": false}} + } + } + """ + self.app_config_mock = Mock() + self.app_config_mock.get_configuration.return_value = {"Content": self.content_stream_mock} + + @parameterized.expand( + [ + param("feature-1", "beta", "default", False), + param("feature-1", "beta", "us-west-2", True), + param("feature-2", "beta", "us-west-2", False), # because feature is missing + ] + ) + @patch("samtranslator.feature_toggle.feature_toggle.boto3") + def test_feature_toggle_for_stage(self, feature_name, stage, region, expected, boto3_mock): + boto3_mock.client.return_value = self.app_config_mock + feature_toggle_config_provider = FeatureToggleAppConfigConfigProvider( + "test_app_id", "test_env_id", "test_conf_id" + ) + feature_toggle = FeatureToggle(feature_toggle_config_provider) + self.assertEqual(feature_toggle.is_enabled_for_stage_in_region(feature_name, stage, region), expected) + + @parameterized.expand( + [ + param("feature-1", "beta", "default", "123456789123", False), + param("feature-1", "beta", "us-west-2", "123456789123", True), + param("feature-2", "beta", "us-west-2", "123456789124", False), # because feature is missing + ] + ) + @patch("samtranslator.feature_toggle.feature_toggle.boto3") + def test_feature_toggle_with_local_provider_for_account_id( + self, feature_name, stage, region, account_id, expected, boto3_mock + ): + boto3_mock.client.return_value = self.app_config_mock + feature_toggle_config_provider = FeatureToggleAppConfigConfigProvider( + "test_app_id", "test_env_id", "test_conf_id" + ) + feature_toggle = FeatureToggle(feature_toggle_config_provider) + self.assertEqual( + feature_toggle.is_enabled_for_account_in_region(feature_name, stage, account_id, region), expected + ) diff --git a/tests/model/eventsources/test_cloudwatch_event_source.py b/tests/model/eventsources/test_cloudwatch_event_source.py new file mode 100644 index 000000000..b323263aa --- /dev/null +++ b/tests/model/eventsources/test_cloudwatch_event_source.py @@ -0,0 +1,24 @@ +from mock import Mock, patch +from unittest import TestCase + +from samtranslator.model.eventsources.push import CloudWatchEvent +from samtranslator.model.lambda_ import LambdaFunction + + +class CloudWatchEventSourceTests(TestCase): + def setUp(self): + self.logical_id = "EventLogicalId" + self.func = LambdaFunction("func") + + def test_target_id_when_not_provided(self): + cloudwatch_event_source = CloudWatchEvent(self.logical_id) + cfn = cloudwatch_event_source.to_cloudformation(function=self.func) + target_id = cfn[0].Targets[0]["Id"] + self.assertEqual(target_id, "{}{}".format(self.logical_id, "LambdaTarget")) + + def test_target_id_when_provided(self): + cloudwatch_event_source = CloudWatchEvent(self.logical_id) + cloudwatch_event_source.Target = {"Id": "MyTargetId"} + cfn = cloudwatch_event_source.to_cloudformation(function=self.func) + target_id = cfn[0].Targets[0]["Id"] + self.assertEqual(target_id, "MyTargetId") diff --git a/tests/model/test_api_v2.py b/tests/model/test_api_v2.py index eb32e711c..22b62aabe 100644 --- a/tests/model/test_api_v2.py +++ b/tests/model/test_api_v2.py @@ -47,13 +47,16 @@ def test_create_authorizer_fails_with_string_authorization_scopes(self): authorization_scopes="invalid_scope", ) self.assertEqual( - e.value.message, "Resource with id [logicalId] is invalid. " + "AuthorizationScopes must be a list.", + e.value.message, + "Resource with id [logicalId] is invalid. " + "AuthorizationScopes must be a list.", ) def test_create_authorizer_fails_with_authorization_scopes_non_oauth2(self): with pytest.raises(InvalidResourceException) as e: ApiGatewayV2Authorizer( - api_logical_id="logicalId", name="authName", authorization_scopes=["scope1", "scope2"], + api_logical_id="logicalId", + name="authName", + authorization_scopes=["scope1", "scope2"], ) self.assertEqual( e.value.message, @@ -67,7 +70,9 @@ def test_create_authorizer_fails_with_authorization_scopes_non_oauth2(self): def test_create_authorizer_fails_with_jtw_configuration_non_oauth2(self): with pytest.raises(InvalidResourceException) as e: ApiGatewayV2Authorizer( - api_logical_id="logicalId", name="authName", jwt_configuration={"config": "value"}, + api_logical_id="logicalId", + name="authName", + jwt_configuration={"config": "value"}, ) self.assertEqual( e.value.message, @@ -78,7 +83,9 @@ def test_create_authorizer_fails_with_jtw_configuration_non_oauth2(self): def test_create_authorizer_fails_with_id_source_non_oauth2(self): with pytest.raises(InvalidResourceException) as e: ApiGatewayV2Authorizer( - api_logical_id="logicalId", name="authName", id_source="https://example.com", + api_logical_id="logicalId", + name="authName", + id_source="https://example.com", ) self.assertEqual( e.value.message, @@ -180,7 +187,8 @@ def test_create_jwt_authorizer_no_id_source(self): def test_create_lambda_auth_no_function_arn(self): with pytest.raises(InvalidResourceException) as e: ApiGatewayV2Authorizer( - api_logical_id="logicalId", name="lambdaAuth", + api_logical_id="logicalId", + name="lambdaAuth", ) self.assertEqual( e.value.message, @@ -190,7 +198,9 @@ def test_create_lambda_auth_no_function_arn(self): def test_create_lambda_auth_no_authorizer_payload_format_version(self): with pytest.raises(InvalidResourceException) as e: ApiGatewayV2Authorizer( - api_logical_id="logicalId", name="lambdaAuth", function_arn="lambdaArn", + api_logical_id="logicalId", + name="lambdaAuth", + function_arn="lambdaArn", ) self.assertEqual( e.value.message, diff --git a/tests/swagger/test_swagger.py b/tests/swagger/test_swagger.py index 7e5641951..295050503 100644 --- a/tests/swagger/test_swagger.py +++ b/tests/swagger/test_swagger.py @@ -1168,7 +1168,12 @@ def test_must_add_vpc_allow_string_only(self): {"Fn::Sub": ["execute-api:/${__Stage__}/GET/foo", {"__Stage__": "prod"}]}, ], "Effect": "Deny", - "Condition": {"StringNotEquals": {"aws:SourceVpc": ["vpc-123"], "aws:SourceVpce": ["vpce-345"],}}, + "Condition": { + "StringNotEquals": { + "aws:SourceVpc": ["vpc-123"], + "aws:SourceVpce": ["vpce-345"], + } + }, "Principal": "*", }, ], diff --git a/tests/test_model.py b/tests/test_model.py index 446e5b575..84da811b8 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -101,8 +101,7 @@ class MyResource(Resource): property_types = {} def test_to_dict(self): - """Tests if resource attributes are correctly set and converted to dictionary - """ + """Tests if resource attributes are correctly set and converted to dictionary""" empty_resource_dict = {"id": {"Type": "foo", "Properties": {}}} dict_with_attributes = { diff --git a/tests/translator/input/amq.yaml b/tests/translator/input/amq.yaml new file mode 100644 index 000000000..00d28b841 --- /dev/null +++ b/tests/translator/input/amq.yaml @@ -0,0 +1,17 @@ +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 \ No newline at end of file diff --git a/tests/translator/input/api_with_basic_custom_domain.yaml b/tests/translator/input/api_with_basic_custom_domain.yaml index 65bbbf4c9..2d8c915b0 100644 --- a/tests/translator/input/api_with_basic_custom_domain.yaml +++ b/tests/translator/input/api_with_basic_custom_domain.yaml @@ -62,6 +62,11 @@ Resources: CertificateArn: 'my-api-cert-arn' EndpointConfiguration: 'EDGE' BasePath: [ "/get", "/fetch" ] + MutualTlsAuthentication: + TruststoreUri: 'my-api-truststore-uri' + TruststoreVersion: 'my-api-truststore-version' + SecurityPolicy: TLS_1_2 + MyAnotherApi: Type: AWS::Serverless::Api diff --git a/tests/translator/input/api_with_basic_custom_domain_http.yaml b/tests/translator/input/api_with_basic_custom_domain_http.yaml index e9a0c5f8a..9172c00c1 100644 --- a/tests/translator/input/api_with_basic_custom_domain_http.yaml +++ b/tests/translator/input/api_with_basic_custom_domain_http.yaml @@ -13,6 +13,10 @@ Globals: DomainName: !Ref MyDomainName CertificateArn: !Ref MyDomainCert EndpointConfiguration: REGIONAL + MutualTlsAuthentication: + TruststoreUri: 'my-api-v2-truststore-uri' + TruststoreVersion: 'my-api-v2-truststore-version' + SecurityPolicy: TLS_1_2 BasePath: ["/basic", "/begin-here"] Route53: HostedZoneName: sam-example.com. @@ -47,4 +51,5 @@ Resources: MyApi: Type: AWS::Serverless::HttpApi Properties: + DisableExecuteApiEndpoint: true StageName: Prod \ No newline at end of file diff --git a/tests/translator/input/api_with_basic_custom_domain_intrinsics.yaml b/tests/translator/input/api_with_basic_custom_domain_intrinsics.yaml index 9f5a103ed..b1d642754 100644 --- a/tests/translator/input/api_with_basic_custom_domain_intrinsics.yaml +++ b/tests/translator/input/api_with_basic_custom_domain_intrinsics.yaml @@ -12,6 +12,14 @@ Parameters: Type: String Default: REGIONAL + MyMTLSUri: + Default: another-api-truststore-uri + Type: String + + MyMTLSVersion: + Default: another-api-truststore-version + Type: String + Resources: MyFunction: Condition: C1 @@ -51,4 +59,8 @@ Resources: CertificateArn: !Ref MyDomainCert EndpointConfiguration: !Ref EndpointConf BasePath: [ "/get", "/fetch" ] + MutualTlsAuthentication: + TruststoreUri: !Ref MyMTLSUri + TruststoreVersion: !Ref MyMTLSVersion + SecurityPolicy: TLS_1_2 diff --git a/tests/translator/input/api_with_basic_custom_domain_intrinsics_http.yaml b/tests/translator/input/api_with_basic_custom_domain_intrinsics_http.yaml index 03cb1323c..c03fac389 100644 --- a/tests/translator/input/api_with_basic_custom_domain_intrinsics_http.yaml +++ b/tests/translator/input/api_with_basic_custom_domain_intrinsics_http.yaml @@ -12,6 +12,14 @@ Parameters: Type: String Default: REGIONAL + MyMTLSUriHTTP: + Default: another-api-v2-truststore-uri + Type: String + + MyMTLSVersionHTTP: + Default: another-api-v2-truststore-version + Type: String + Resources: MyFunction: Condition: C1 @@ -50,4 +58,8 @@ Resources: CertificateArn: !Ref MyDomainCert EndpointConfiguration: !Ref EndpointConf BasePath: [ "/get", "/fetch" ] + MutualTlsAuthentication: + TruststoreUri: !Ref MyMTLSUriHTTP + TruststoreVersion: !Ref MyMTLSVersionHTTP + SecurityPolicy: TLS_1_2 diff --git a/tests/translator/input/error_api_mtls_configuration_invalid_field.yaml b/tests/translator/input/error_api_mtls_configuration_invalid_field.yaml new file mode 100644 index 000000000..96c465199 --- /dev/null +++ b/tests/translator/input/error_api_mtls_configuration_invalid_field.yaml @@ -0,0 +1,16 @@ +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: Prod + Domain: + DomainName: 'api-example.com' + CertificateArn: 'my-api-cert-arn' + EndpointConfiguration: 'EDGE' + BasePath: [ "/get", "/fetch" ] + MutualTlsAuthentication: + WrongTruststoreUri: 'my-api-truststore-uri' + TruststoreVersion: 'my-api-truststore-version' + SecurityPolicy: TLS_1_2 + diff --git a/tests/translator/input/error_api_mtls_configuration_invalid_type.yaml b/tests/translator/input/error_api_mtls_configuration_invalid_type.yaml new file mode 100644 index 000000000..dcc2faee0 --- /dev/null +++ b/tests/translator/input/error_api_mtls_configuration_invalid_type.yaml @@ -0,0 +1,16 @@ +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: Prod + Domain: + DomainName: 'api-example.com' + CertificateArn: 'my-api-cert-arn' + EndpointConfiguration: 'EDGE' + BasePath: [ "/get", "/fetch" ] + MutualTlsAuthentication: + - "TruststoreUri" + - "TruststoreVersion" + SecurityPolicy: TLS_1_2 + diff --git a/tests/translator/input/error_httpapi_mtls_configuration_invalid_field.yaml b/tests/translator/input/error_httpapi_mtls_configuration_invalid_field.yaml new file mode 100644 index 000000000..38259bed7 --- /dev/null +++ b/tests/translator/input/error_httpapi_mtls_configuration_invalid_field.yaml @@ -0,0 +1,17 @@ +Resources: + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + DisableExecuteApiEndpoint: true + StageName: Prod + Domain: + DomainName: "sam-example.com" + CertificateArn: "arn:aws:acm:us-east-1:123455353535:certificate/6c911401-620d-4d41-b89e-366c238bb2f3" + EndpointConfiguration: REGIONAL + MutualTlsAuthentication: + WrongTruststoreUri: 'my-api-v2-truststore-uri' + TruststoreVersionWrong: 'my-api-v2-truststore-version' + SecurityPolicy: TLS_1_2 + BasePath: ["/basic", "/begin-here"] + Route53: + HostedZoneName: sam-example.com. \ No newline at end of file diff --git a/tests/translator/input/error_httpapi_mtls_configuration_invalid_type.yaml b/tests/translator/input/error_httpapi_mtls_configuration_invalid_type.yaml new file mode 100644 index 000000000..cd3dbe89b --- /dev/null +++ b/tests/translator/input/error_httpapi_mtls_configuration_invalid_type.yaml @@ -0,0 +1,17 @@ +Resources: + MyApi: + Type: AWS::Serverless::HttpApi + Properties: + DisableExecuteApiEndpoint: true + StageName: Prod + Domain: + DomainName: "sam-example.com" + CertificateArn: "arn:aws:acm:us-east-1:123455353535:certificate/6c911401-620d-4d41-b89e-366c238bb2f3" + EndpointConfiguration: REGIONAL + MutualTlsAuthentication: + - "TruststoreUri" + - "TruststoreVersion" + SecurityPolicy: TLS_1_2 + BasePath: ["/basic", "/begin-here"] + Route53: + HostedZoneName: sam-example.com. \ No newline at end of file diff --git a/tests/translator/input/error_missing_broker.yaml b/tests/translator/input/error_missing_broker.yaml new file mode 100644 index 000000000..143b2ca27 --- /dev/null +++ b/tests/translator/input/error_missing_broker.yaml @@ -0,0 +1,16 @@ +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: + Queues: + - "Queue1" + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c \ No newline at end of file diff --git a/tests/translator/output/amq.json b/tests/translator/output/amq.json new file mode 100644 index 000000000..796785adc --- /dev/null +++ b/tests/translator/output/amq.json @@ -0,0 +1,70 @@ +{ + "Resources": { + "MQFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "queues.zip" + }, + "Handler": "queue.mq_handler", + "Role": { + "Fn::GetAtt": [ + "MQFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "MQFunctionMyMQQueue": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "EventSourceArn": "arn:aws:mq:us-east-2:123456789012:broker:MyBroker:b-1234a5b6-78cd-901e-2fgh-3i45j6k178l9", + "FunctionName": { + "Ref": "MQFunction" + }, + "Queues": ["Queue1"], + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ] + } + }, + "MQFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaAMQExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_basic_custom_domain.json b/tests/translator/output/api_with_basic_custom_domain.json index 4d7cb5b79..4ed229a5a 100644 --- a/tests/translator/output/api_with_basic_custom_domain.json +++ b/tests/translator/output/api_with_basic_custom_domain.json @@ -61,7 +61,12 @@ "Types": [ "EDGE" ] - }, + }, + "MutualTlsAuthentication": { + "TruststoreUri": "my-api-truststore-uri", + "TruststoreVersion": "my-api-truststore-version" + }, + "SecurityPolicy": "TLS_1_2", "DomainName": "api-example.com" } }, @@ -268,7 +273,7 @@ "Types": [ "REGIONAL" ] - }, + }, "RegionalCertificateArn": "another-api-arn", "DomainName": "another-example.com" } @@ -295,13 +300,13 @@ "StageName": "Prod" } }, - "MyApiDeploymentbfb0243af9": { + "MyApiDeployment347043ff9e": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "MyApi" }, - "Description": "RestApi deployment id: bfb0243af9bffaabaf3bbc9d980266c6d6eb8471" + "Description": "RestApi deployment id: 347043ff9ec72c6ce10317548939c3802eb1dd55" } }, "ServerlessRestApiBasePathMapping": { @@ -322,7 +327,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "MyApiDeploymentbfb0243af9" + "Ref": "MyApiDeployment347043ff9e" }, "RestApiId": { "Ref": "MyApi" diff --git a/tests/translator/output/api_with_basic_custom_domain_http.json b/tests/translator/output/api_with_basic_custom_domain_http.json index f9bb82d27..df21bd4d7 100644 --- a/tests/translator/output/api_with_basic_custom_domain_http.json +++ b/tests/translator/output/api_with_basic_custom_domain_http.json @@ -32,9 +32,14 @@ "DomainNameConfigurations": [ { "CertificateArn": "arn:aws:acm:us-east-1:123455353535:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", - "EndpointType": "REGIONAL" + "EndpointType": "REGIONAL", + "SecurityPolicy": "TLS_1_2" } - ], + ], + "MutualTlsAuthentication": { + "TruststoreUri": "my-api-v2-truststore-uri", + "TruststoreVersion": "my-api-v2-truststore-version" + }, "Tags": { "httpapi:createdBy": "SAM" } @@ -170,6 +175,7 @@ "MyApi": { "Type": "AWS::ApiGatewayV2::Api", "Properties": { + "DisableExecuteApiEndpoint": true, "Body": { "info": { "version": "1.0", diff --git a/tests/translator/output/api_with_basic_custom_domain_intrinsics.json b/tests/translator/output/api_with_basic_custom_domain_intrinsics.json index d763bf30f..09d694c4e 100644 --- a/tests/translator/output/api_with_basic_custom_domain_intrinsics.json +++ b/tests/translator/output/api_with_basic_custom_domain_intrinsics.json @@ -15,6 +15,14 @@ "EndpointConf": { "Default": "REGIONAL", "Type": "String" + }, + "MyMTLSUri": { + "Default": "another-api-truststore-uri", + "Type": "String" + }, + "MyMTLSVersion": { + "Default": "another-api-truststore-version", + "Type": "String" } }, "Resources": { @@ -74,8 +82,13 @@ "RegionalCertificateArn": "another-api-arn", "DomainName": { "Fn::Sub": "example-ap-southeast-1.com" - } - }, + }, + "MutualTlsAuthentication": { + "TruststoreUri": "another-api-truststore-uri", + "TruststoreVersion": "another-api-truststore-version" + }, + "SecurityPolicy": "TLS_1_2" + }, "Condition": "C1" }, "ServerlessRestApiProdStage": { @@ -222,7 +235,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "MyApiDeployment462f07ab8f" + "Ref": "MyApiDeployment19c8cf5c63" }, "RestApiId": { "Ref": "MyApi" @@ -231,13 +244,13 @@ }, "Condition": "C1" }, - "MyApiDeployment462f07ab8f": { + "MyApiDeployment19c8cf5c63": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "MyApi" }, - "Description": "RestApi deployment id: 462f07ab8fcdc437955985b08464280d2a9244be" + "Description": "RestApi deployment id: 19c8cf5c63090f12c5a96f6f57162495bed446c7" }, "Condition": "C1" }, diff --git a/tests/translator/output/api_with_basic_custom_domain_intrinsics_http.json b/tests/translator/output/api_with_basic_custom_domain_intrinsics_http.json index dca8fe2e7..008f09ebe 100644 --- a/tests/translator/output/api_with_basic_custom_domain_intrinsics_http.json +++ b/tests/translator/output/api_with_basic_custom_domain_intrinsics_http.json @@ -15,6 +15,14 @@ "EndpointConf": { "Default": "REGIONAL", "Type": "String" + }, + "MyMTLSUriHTTP": { + "Default": "another-api-v2-truststore-uri", + "Type": "String" + }, + "MyMTLSVersionHTTP": { + "Default": "another-api-v2-truststore-version", + "Type": "String" } }, "Resources": { @@ -261,9 +269,14 @@ "DomainNameConfigurations": [ { "CertificateArn": "another-api-arn", - "EndpointType": "REGIONAL" + "EndpointType": "REGIONAL", + "SecurityPolicy": "TLS_1_2" } - ], + ], + "MutualTlsAuthentication": { + "TruststoreUri": "another-api-v2-truststore-uri", + "TruststoreVersion": "another-api-v2-truststore-version" + }, "Tags": { "httpapi:createdBy": "SAM" } diff --git a/tests/translator/output/aws-cn/amq.json b/tests/translator/output/aws-cn/amq.json new file mode 100644 index 000000000..b75267663 --- /dev/null +++ b/tests/translator/output/aws-cn/amq.json @@ -0,0 +1,70 @@ +{ + "Resources": { + "MQFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "queues.zip" + }, + "Handler": "queue.mq_handler", + "Role": { + "Fn::GetAtt": [ + "MQFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "MQFunctionMyMQQueue": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "EventSourceArn": "arn:aws:mq:us-east-2:123456789012:broker:MyBroker:b-1234a5b6-78cd-901e-2fgh-3i45j6k178l9", + "FunctionName": { + "Ref": "MQFunction" + }, + "Queues": ["Queue1"], + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ] + } + }, + "MQFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaAMQExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain.json index 027571d85..88e64e6a8 100644 --- a/tests/translator/output/aws-cn/api_with_basic_custom_domain.json +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain.json @@ -57,7 +57,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "MyApiDeployment062deb3f92" + "Ref": "MyApiDeployment92d6d51a5e" }, "RestApiId": { "Ref": "MyApi" @@ -284,7 +284,7 @@ "Types": [ "REGIONAL" ] - }, + }, "RegionalCertificateArn": "another-api-arn", "DomainName": "another-example.com" } @@ -297,7 +297,12 @@ "Types": [ "EDGE" ] - }, + }, + "MutualTlsAuthentication": { + "TruststoreUri": "my-api-truststore-uri", + "TruststoreVersion": "my-api-truststore-version" + }, + "SecurityPolicy": "TLS_1_2", "DomainName": "api-example.com" } }, @@ -313,13 +318,13 @@ "StageName": "Prod" } }, - "MyApiDeployment062deb3f92": { + "MyApiDeployment92d6d51a5e": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "MyApi" }, - "Description": "RestApi deployment id: 062deb3f92aa9993cabcc1ae62b31c0411a7c2ce" + "Description": "RestApi deployment id: 92d6d51a5e324a2836c79d3645d7e678f063037e" } }, "ServerlessRestApiBasePathMapping": { diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain_http.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain_http.json index 08b0bea50..0379bfbf4 100644 --- a/tests/translator/output/aws-cn/api_with_basic_custom_domain_http.json +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain_http.json @@ -32,9 +32,14 @@ "DomainNameConfigurations": [ { "CertificateArn": "arn:aws:acm:us-east-1:123455353535:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", - "EndpointType": "REGIONAL" + "EndpointType": "REGIONAL", + "SecurityPolicy": "TLS_1_2" } - ], + ], + "MutualTlsAuthentication": { + "TruststoreUri": "my-api-v2-truststore-uri", + "TruststoreVersion": "my-api-v2-truststore-version" + }, "Tags": { "httpapi:createdBy": "SAM" } @@ -170,6 +175,7 @@ "MyApi": { "Type": "AWS::ApiGatewayV2::Api", "Properties": { + "DisableExecuteApiEndpoint": true, "Body": { "info": { "version": "1.0", diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics.json index 2550babe9..a7c6e267e 100644 --- a/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics.json +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics.json @@ -15,6 +15,14 @@ "EndpointConf": { "Default": "REGIONAL", "Type": "String" + }, + "MyMTLSUri": { + "Default": "another-api-truststore-uri", + "Type": "String" + }, + "MyMTLSVersion": { + "Default": "another-api-truststore-version", + "Type": "String" } }, "Resources": { @@ -79,13 +87,13 @@ }, "Condition": "C1" }, - "MyApiDeployment39d61c7d9b": { + "MyApiDeployment4f2c19d290": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "MyApi" }, - "Description": "RestApi deployment id: 39d61c7d9b47f6d3002c3e6706e1862ec03914dc" + "Description": "RestApi deployment id: 4f2c19d290875d88d8e30124f0953f1784e1b54d" }, "Condition": "C1" }, @@ -225,7 +233,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "MyApiDeployment39d61c7d9b" + "Ref": "MyApiDeployment4f2c19d290" }, "RestApiId": { "Ref": "MyApi" @@ -241,7 +249,12 @@ "Types": [ "REGIONAL" ] - }, + }, + "MutualTlsAuthentication": { + "TruststoreUri": "another-api-truststore-uri", + "TruststoreVersion": "another-api-truststore-version" + }, + "SecurityPolicy": "TLS_1_2", "RegionalCertificateArn": "another-api-arn", "DomainName": { "Fn::Sub": "example-cn-north-1.com" diff --git a/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics_http.json b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics_http.json index 02d0cb976..890a4154e 100644 --- a/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics_http.json +++ b/tests/translator/output/aws-cn/api_with_basic_custom_domain_intrinsics_http.json @@ -15,6 +15,14 @@ "EndpointConf": { "Default": "REGIONAL", "Type": "String" + }, + "MyMTLSUriHTTP": { + "Default": "another-api-v2-truststore-uri", + "Type": "String" + }, + "MyMTLSVersionHTTP": { + "Default": "another-api-v2-truststore-version", + "Type": "String" } }, "Resources": { @@ -174,9 +182,14 @@ "DomainNameConfigurations": [ { "CertificateArn": "another-api-arn", - "EndpointType": "REGIONAL" + "EndpointType": "REGIONAL", + "SecurityPolicy": "TLS_1_2" } - ], + ], + "MutualTlsAuthentication": { + "TruststoreUri": "another-api-v2-truststore-uri", + "TruststoreVersion": "another-api-v2-truststore-version" + }, "DomainName": { "Fn::Sub": "example-cn-north-1.com" }, diff --git a/tests/translator/output/aws-us-gov/amq.json b/tests/translator/output/aws-us-gov/amq.json new file mode 100644 index 000000000..5a42ee959 --- /dev/null +++ b/tests/translator/output/aws-us-gov/amq.json @@ -0,0 +1,70 @@ +{ + "Resources": { + "MQFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "queues.zip" + }, + "Handler": "queue.mq_handler", + "Role": { + "Fn::GetAtt": [ + "MQFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "MQFunctionMyMQQueue": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "EventSourceArn": "arn:aws:mq:us-east-2:123456789012:broker:MyBroker:b-1234a5b6-78cd-901e-2fgh-3i45j6k178l9", + "FunctionName": { + "Ref": "MQFunction" + }, + "Queues": ["Queue1"], + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ] + } + }, + "MQFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaAMQExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain.json index 58ad15e60..8d04935fe 100644 --- a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain.json +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain.json @@ -61,7 +61,12 @@ "Types": [ "EDGE" ] - }, + }, + "MutualTlsAuthentication": { + "TruststoreUri": "my-api-truststore-uri", + "TruststoreVersion": "my-api-truststore-version" + }, + "SecurityPolicy": "TLS_1_2", "DomainName": "api-example.com" } }, @@ -210,13 +215,13 @@ ] } }, - "MyApiDeploymentdf7be0d48e": { + "MyApiDeploymentb34773e43a": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "MyApi" }, - "Description": "RestApi deployment id: df7be0d48edfc5455fdd83e8eec35cf2e9956dc5" + "Description": "RestApi deployment id: b34773e43ad9a98f27deed4374d3a49449abd947" } }, "MyApi": { @@ -288,7 +293,7 @@ "Types": [ "REGIONAL" ] - }, + }, "RegionalCertificateArn": "another-api-arn", "DomainName": "another-example.com" } @@ -333,7 +338,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "MyApiDeploymentdf7be0d48e" + "Ref": "MyApiDeploymentb34773e43a" }, "RestApiId": { "Ref": "MyApi" diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_http.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_http.json index cb2577deb..15be22192 100644 --- a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_http.json +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_http.json @@ -32,9 +32,14 @@ "DomainNameConfigurations": [ { "CertificateArn": "arn:aws:acm:us-east-1:123455353535:certificate/6c911401-620d-4d41-b89e-366c238bb2f3", - "EndpointType": "REGIONAL" + "EndpointType": "REGIONAL", + "SecurityPolicy": "TLS_1_2" } - ], + ], + "MutualTlsAuthentication": { + "TruststoreUri": "my-api-v2-truststore-uri", + "TruststoreVersion": "my-api-v2-truststore-version" + }, "Tags": { "httpapi:createdBy": "SAM" } @@ -170,6 +175,7 @@ "MyApi": { "Type": "AWS::ApiGatewayV2::Api", "Properties": { + "DisableExecuteApiEndpoint": true, "Body": { "info": { "version": "1.0", @@ -206,7 +212,7 @@ } } }, - "openapi": "3.0.1", + "openapi": "3.0.1", "tags": [ { "name": "httpapi:createdBy", diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics.json index 9fa7dad61..8d1fbf5bb 100644 --- a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics.json +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics.json @@ -15,6 +15,14 @@ "EndpointConf": { "Default": "REGIONAL", "Type": "String" + }, + "MyMTLSUri": { + "Default": "another-api-truststore-uri", + "Type": "String" + }, + "MyMTLSVersion": { + "Default": "another-api-truststore-version", + "Type": "String" } }, "Resources": { @@ -150,13 +158,13 @@ }, "Condition": "C1" }, - "MyApiDeploymentc2e138c194": { + "MyApiDeployment32e59613e2": { "Type": "AWS::ApiGateway::Deployment", "Properties": { "RestApiId": { "Ref": "MyApi" }, - "Description": "RestApi deployment id: c2e138c1943ffe20492a6a242e7537c4cd3c35f9" + "Description": "RestApi deployment id: 32e59613e2e02a1f1d264849167ea359f10342f0" }, "Condition": "C1" }, @@ -225,7 +233,7 @@ "Type": "AWS::ApiGateway::Stage", "Properties": { "DeploymentId": { - "Ref": "MyApiDeploymentc2e138c194" + "Ref": "MyApiDeployment32e59613e2" }, "RestApiId": { "Ref": "MyApi" @@ -241,7 +249,12 @@ "Types": [ "REGIONAL" ] - }, + }, + "MutualTlsAuthentication": { + "TruststoreUri": "another-api-truststore-uri", + "TruststoreVersion": "another-api-truststore-version" + }, + "SecurityPolicy": "TLS_1_2", "RegionalCertificateArn": "another-api-arn", "DomainName": { "Fn::Sub": "example-us-gov-west-1.com" diff --git a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics_http.json b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics_http.json index b3a6b181c..72fb56c8a 100644 --- a/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics_http.json +++ b/tests/translator/output/aws-us-gov/api_with_basic_custom_domain_intrinsics_http.json @@ -15,6 +15,14 @@ "EndpointConf": { "Default": "REGIONAL", "Type": "String" + }, + "MyMTLSUriHTTP": { + "Default": "another-api-v2-truststore-uri", + "Type": "String" + }, + "MyMTLSVersionHTTP": { + "Default": "another-api-v2-truststore-version", + "Type": "String" } }, "Resources": { @@ -177,9 +185,14 @@ "DomainNameConfigurations": [ { "CertificateArn": "another-api-arn", - "EndpointType": "REGIONAL" + "EndpointType": "REGIONAL", + "SecurityPolicy": "TLS_1_2" } - ], + ], + "MutualTlsAuthentication": { + "TruststoreUri": "another-api-v2-truststore-uri", + "TruststoreVersion": "another-api-v2-truststore-version" + }, "Tags": { "httpapi:createdBy": "SAM" } diff --git a/tests/translator/output/error_api_mtls_configuration_invalid_field.json b/tests/translator/output/error_api_mtls_configuration_invalid_field.json new file mode 100644 index 000000000..ffbf6bf73 --- /dev/null +++ b/tests/translator/output/error_api_mtls_configuration_invalid_field.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [WrongTruststoreUri] is invalid. MutualTlsAuthentication must contains one of the following fields ['TruststoreUri', 'TruststoreVersion']." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [WrongTruststoreUri] is invalid. Available MutualTlsAuthentication fields are ['TruststoreUri', 'TruststoreVersion']." +} \ No newline at end of file diff --git a/tests/translator/output/error_api_mtls_configuration_invalid_type.json b/tests/translator/output/error_api_mtls_configuration_invalid_type.json new file mode 100644 index 000000000..28b0e56bf --- /dev/null +++ b/tests/translator/output/error_api_mtls_configuration_invalid_type.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [['TruststoreUri', 'TruststoreVersion']] is invalid. MutualTlsAuthentication must contains one of the following fields ['TruststoreUri', 'TruststoreVersion']." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [['TruststoreUri', 'TruststoreVersion']] is invalid. MutualTlsAuthentication must be a map with at least one of the following fields ['TruststoreUri', 'TruststoreVersion']." +} \ No newline at end of file diff --git a/tests/translator/output/error_httpapi_mtls_configuration_invalid_field.json b/tests/translator/output/error_httpapi_mtls_configuration_invalid_field.json new file mode 100644 index 000000000..4aa5f273b --- /dev/null +++ b/tests/translator/output/error_httpapi_mtls_configuration_invalid_field.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [TruststoreVersionWrong,WrongTruststoreUri] is invalid. Available MutualTlsAuthentication fields are ['TruststoreUri', 'TruststoreVersion']." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [TruststoreVersionWrong,WrongTruststoreUri] is invalid. Available MutualTlsAuthentication fields are ['TruststoreUri', 'TruststoreVersion']." +} \ No newline at end of file diff --git a/tests/translator/output/error_httpapi_mtls_configuration_invalid_type.json b/tests/translator/output/error_httpapi_mtls_configuration_invalid_type.json new file mode 100644 index 000000000..28b0e56bf --- /dev/null +++ b/tests/translator/output/error_httpapi_mtls_configuration_invalid_type.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [['TruststoreUri', 'TruststoreVersion']] is invalid. MutualTlsAuthentication must contains one of the following fields ['TruststoreUri', 'TruststoreVersion']." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [['TruststoreUri', 'TruststoreVersion']] is invalid. MutualTlsAuthentication must be a map with at least one of the following fields ['TruststoreUri', 'TruststoreVersion']." +} \ No newline at end of file diff --git a/tests/translator/output/error_missing_broker.json b/tests/translator/output/error_missing_broker.json new file mode 100644 index 000000000..136ac3800 --- /dev/null +++ b/tests/translator/output/error_missing_broker.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for ActiveMQ) provided." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for ActiveMQ) provided." +} \ No newline at end of file diff --git a/tests/translator/output/error_missing_queue.json b/tests/translator/output/error_missing_queue.json index 561dfefcf..4a7ba1f7a 100644 --- a/tests/translator/output/error_missing_queue.json +++ b/tests/translator/output/error_missing_queue.json @@ -1,6 +1,6 @@ { "errors": [{ - "errorMessage": "Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." + "errorMessage": "Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for ActiveMQ) provided." }], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for ActiveMQ) provided." } \ No newline at end of file diff --git a/tests/translator/output/error_missing_stream.json b/tests/translator/output/error_missing_stream.json index 3dd94a47e..f85ff25e8 100644 --- a/tests/translator/output/error_missing_stream.json +++ b/tests/translator/output/error_missing_stream.json @@ -1,6 +1,6 @@ { "errors": [{ - "errorMessage": "Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." + "errorMessage": "Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for ActiveMQ) provided." }], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) provided." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for ActiveMQ) provided." } \ No newline at end of file diff --git a/tests/translator/test_function_resources.py b/tests/translator/test_function_resources.py index bfa0cda1e..f5c8c2ab1 100644 --- a/tests/translator/test_function_resources.py +++ b/tests/translator/test_function_resources.py @@ -550,8 +550,10 @@ def test_get_resolved_alias_name_must_work(self): def test_get_resolved_alias_name_must_error_if_intrinsics_are_not_resolved(self): property_name = "something" - expected_exception_msg = "Resource with id [{}] is invalid. '{}' must be a string or a Ref to a template parameter".format( - self.sam_func.logical_id, property_name + expected_exception_msg = ( + "Resource with id [{}] is invalid. '{}' must be a string or a Ref to a template parameter".format( + self.sam_func.logical_id, property_name + ) ) alias_value = {"Ref": "param1"} @@ -567,8 +569,10 @@ def test_get_resolved_alias_name_must_error_if_intrinsics_are_not_resolved(self) def test_get_resolved_alias_name_must_error_if_intrinsics_are_not_resolved_with_list(self): property_name = "something" - expected_exception_msg = "Resource with id [{}] is invalid. '{}' must be a string or a Ref to a template parameter".format( - self.sam_func.logical_id, property_name + expected_exception_msg = ( + "Resource with id [{}] is invalid. '{}' must be a string or a Ref to a template parameter".format( + self.sam_func.logical_id, property_name + ) ) alias_value = ["Ref", "param1"] diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 2ee086381..deedc6336 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -157,6 +157,7 @@ class TestTranslatorEndToEnd(TestCase): "cloudwatchlog", "streams", "sqs", + "amq", "simpletable", "simpletable_with_sse", "implicit_api", @@ -604,6 +605,7 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw "error_function_invalid_request_parameters", "error_invalid_logical_id", "error_layer_invalid_properties", + "error_missing_broker", "error_missing_queue", "error_missing_startingposition", "error_missing_stream", @@ -653,6 +655,10 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw "error_api_with_usage_plan_invalid_parameter", "error_http_api_with_cors_def_uri", "error_http_api_invalid_lambda_auth", + "error_api_mtls_configuration_invalid_field", + "error_api_mtls_configuration_invalid_type", + "error_httpapi_mtls_configuration_invalid_field", + "error_httpapi_mtls_configuration_invalid_type", ], ) @patch("boto3.session.Session.region_name", "ap-southeast-1") diff --git a/tox.ini b/tox.ini index 62a8b9fe7..2df512827 100644 --- a/tox.ini +++ b/tox.ini @@ -19,6 +19,8 @@ envlist = py27, py36, py37, py38 # specifically for Py27 in Tox. passenv = AWS* setenv = PYTHONHASHSEED = 0 +commands = make pr2.7 + codecov [testenv] commands = make pr