From a4a063360b1276fdcf74f60c933afaa1a3d07295 Mon Sep 17 00:00:00 2001 From: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:45:08 -0700 Subject: [PATCH] feat: add Terraform support to V2 Api gateway resources (#5479) * feat: Enable hook-name and skip-prepare-infra flagf for sam local start-api (#5217) * Enable hook-name flag for sam local start-api * Format files * test: Terraform local start-api integration tests base (#5240) * feat: update SAM CLI with latest App Templates commit hash (#5211) * feat: updating app templates repo hash with (a34f563f067e13df3eb350d36461b99397b6cda6) * dummy change to trigger checks * revert dummy commit --------- Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Enable hook-name flag for sam local start-api * Format files * fix: fix failing Terraform integration test cases (#5218) * fix: fix the failing terraform integration test cases * fix: fix the resource address while accessing the module config resources * fix: fix checking the experimental log integration test cases * chore: bump version to 1.85.0 (#5226) * chore: use the SAR Application created in testing accounts (#5221) * chore: update aws_lambda_builders to 1.32.0 (#5215) Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Added linking Gateway Method to Lambda Authorizer (#5228) * Added linking method to authorizer * Fixed docstring spelling mistake --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Return early during linking if no destination resources are found (#5220) * Returns during linking if no destination resources are found * Updated comment to correctly reflect state * Cleaned extra word --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * chore: Strengthen wording on "no Auth" during deploy (#5231) Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Link Lambda Authorizer to Rest API (#5219) * Link RestApiId property for Lambda Authorizers * Updated docstring * Format files --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Terraform start-api integration tests * Add test files * Uncomment skip --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Added OpenApi body integration testing and updated property builder (#5291) * Added OpenApi body integration testing and updated property builder * Added more test cases * Changed tearDown to tearDownClass * Updated JSON body parser to handle parsing errors and added unit tests * Removed V1 references * feat: Terraform Authorizer resource testing (#5270) * Added authorizer project * Added project files * Removed extra print * Add request based authorizer testing * test: Test the unsupported limitations for local start api (#5309) * test: Test the unsupported limitations for local start api * fix lint issues * apply pr comments * fix: Bug Bash UX Issues (#5387) * Fix bug bash UX issues * Fix beta warning printing extra characters * Fix authorizer logging * feat: Read aws_apigatewayv2_api and aws_apigatewayv2_route (#5428) * chore(deps): bump actions/setup-go from 3 to 4 (#5418) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * Merge changes from upstream * Read cors props * Tests for apigw v2 api * Tests for apigw v2 route * Add docstrings and type hints --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * chore: Merge develop into feat/terraform-v2-api (#5442) * chore(deps): bump actions/setup-go from 3 to 4 (#5418) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * chore(deps-dev): bump filelock from 3.12.0 to 3.12.2 in /requirements (#5378) Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.12.0 to 3.12.2. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.12.0...3.12.2) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: updating app templates repo hash with (bb905c379830c3d8edbc196bda731076549028e3) (#5398) Co-authored-by: GitHub Action * fix: add a table for package help text. (#5298) * fix: add a table for package help text. * Update samcli/commands/package/core/command.py Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> * tests: fix strings in package help text * fix: PR comments * fix: PR comments. --------- Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> * fix: Handle BROKEN_PIPE_ERROR (#5386) * Handle pywintypes pipe exception * Moved exception checking to check for winerror * Use decorator and added unit tests * Added failure test case * make format * Added more context/comments * fix: remove circular dependency by moving parse_s3 method to its own util file (#5430) * fix: remove circular dependency by moving parse_s3 method to its own util file * add missing unit tests file * chore(deps): bump sympy from 1.10.1 to 1.12 in /requirements (#5338) Bumps [sympy](https://github.com/sympy/sympy) from 1.10.1 to 1.12. - [Release notes](https://github.com/sympy/sympy/releases) - [Commits](https://github.com/sympy/sympy/compare/sympy-1.10.1...sympy-1.12) --- updated-dependencies: - dependency-name: sympy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> * chore(deps): bump websocket-client from 1.5.1 to 1.6.1 in /requirements (#5417) Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.5.1 to 1.6.1. - [Release notes](https://github.com/websocket-client/websocket-client/releases) - [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog) - [Commits](https://github.com/websocket-client/websocket-client/compare/v1.5.1...v1.6.1) --- updated-dependencies: - dependency-name: websocket-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump ruamel-yaml from 0.17.21 to 0.17.32 in /requirements (#5376) * chore(deps): bump ruamel-yaml from 0.17.21 to 0.17.32 in /requirements Bumps [ruamel-yaml](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree) from 0.17.21 to 0.17.32. --- updated-dependencies: - dependency-name: ruamel-yaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Pin ruamel-yaml-clib version --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> Co-authored-by: Haresh Nasit * Updated package formatter to import package options instead of deploy (#5433) Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * chore(deps): bump importlib-metadata in /requirements (#5437) Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.1.0 to 6.7.0. - [Release notes](https://github.com/python/importlib_metadata/releases) - [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst) - [Commits](https://github.com/python/importlib_metadata/compare/v6.1.0...v6.7.0) --- updated-dependencies: - dependency-name: importlib-metadata dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: `sam logs` help text (#5397) * feat: `sam logs` help text * fix: make ruff happy * fix: address comments * feat: enable terraform support for local start-api command (#5389) * feat: Enable hook-name and skip-prepare-infra flagf for sam local start-api (#5217) * Enable hook-name flag for sam local start-api * Format files * test: Terraform local start-api integration tests base (#5240) * feat: update SAM CLI with latest App Templates commit hash (#5211) * feat: updating app templates repo hash with (a34f563f067e13df3eb350d36461b99397b6cda6) * dummy change to trigger checks * revert dummy commit --------- Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Enable hook-name flag for sam local start-api * Format files * fix: fix failing Terraform integration test cases (#5218) * fix: fix the failing terraform integration test cases * fix: fix the resource address while accessing the module config resources * fix: fix checking the experimental log integration test cases * chore: bump version to 1.85.0 (#5226) * chore: use the SAR Application created in testing accounts (#5221) * chore: update aws_lambda_builders to 1.32.0 (#5215) Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Added linking Gateway Method to Lambda Authorizer (#5228) * Added linking method to authorizer * Fixed docstring spelling mistake --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Return early during linking if no destination resources are found (#5220) * Returns during linking if no destination resources are found * Updated comment to correctly reflect state * Cleaned extra word --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * chore: Strengthen wording on "no Auth" during deploy (#5231) Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Link Lambda Authorizer to Rest API (#5219) * Link RestApiId property for Lambda Authorizers * Updated docstring * Format files --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Terraform start-api integration tests * Add test files * Uncomment skip --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Added OpenApi body integration testing and updated property builder (#5291) * Added OpenApi body integration testing and updated property builder * Added more test cases * Changed tearDown to tearDownClass * Updated JSON body parser to handle parsing errors and added unit tests * Removed V1 references * feat: Terraform Authorizer resource testing (#5270) * Added authorizer project * Added project files * Removed extra print * Add request based authorizer testing * test: Test the unsupported limitations for local start api (#5309) * test: Test the unsupported limitations for local start api * fix lint issues * apply pr comments * fix: Bug Bash UX Issues (#5387) * Fix bug bash UX issues * Fix beta warning printing extra characters * Fix authorizer logging --------- Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * Updated warning message about missing function in template (#5347) Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> * chore(deps-dev): bump types-pywin32 in /requirements (#5436) Bumps [types-pywin32](https://github.com/python/typeshed) from 306.0.0.0 to 306.0.0.2. - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-pywin32 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> Co-authored-by: Haresh Nasit Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> * feat: Read APIGW V2 stage and integration resources (#5443) * chore(deps): bump actions/setup-go from 3 to 4 (#5418) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * chore(deps-dev): bump filelock from 3.12.0 to 3.12.2 in /requirements (#5378) Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.12.0 to 3.12.2. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.12.0...3.12.2) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: updating app templates repo hash with (bb905c379830c3d8edbc196bda731076549028e3) (#5398) Co-authored-by: GitHub Action * fix: add a table for package help text. (#5298) * fix: add a table for package help text. * Update samcli/commands/package/core/command.py Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> * tests: fix strings in package help text * fix: PR comments * fix: PR comments. --------- Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> * fix: Handle BROKEN_PIPE_ERROR (#5386) * Handle pywintypes pipe exception * Moved exception checking to check for winerror * Use decorator and added unit tests * Added failure test case * make format * Added more context/comments * fix: remove circular dependency by moving parse_s3 method to its own util file (#5430) * fix: remove circular dependency by moving parse_s3 method to its own util file * add missing unit tests file * chore(deps): bump sympy from 1.10.1 to 1.12 in /requirements (#5338) Bumps [sympy](https://github.com/sympy/sympy) from 1.10.1 to 1.12. - [Release notes](https://github.com/sympy/sympy/releases) - [Commits](https://github.com/sympy/sympy/compare/sympy-1.10.1...sympy-1.12) --- updated-dependencies: - dependency-name: sympy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> * chore(deps): bump websocket-client from 1.5.1 to 1.6.1 in /requirements (#5417) Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.5.1 to 1.6.1. - [Release notes](https://github.com/websocket-client/websocket-client/releases) - [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog) - [Commits](https://github.com/websocket-client/websocket-client/compare/v1.5.1...v1.6.1) --- updated-dependencies: - dependency-name: websocket-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump ruamel-yaml from 0.17.21 to 0.17.32 in /requirements (#5376) * chore(deps): bump ruamel-yaml from 0.17.21 to 0.17.32 in /requirements Bumps [ruamel-yaml](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree) from 0.17.21 to 0.17.32. --- updated-dependencies: - dependency-name: ruamel-yaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Pin ruamel-yaml-clib version --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> Co-authored-by: Haresh Nasit * Updated package formatter to import package options instead of deploy (#5433) Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * chore(deps): bump importlib-metadata in /requirements (#5437) Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.1.0 to 6.7.0. - [Release notes](https://github.com/python/importlib_metadata/releases) - [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst) - [Commits](https://github.com/python/importlib_metadata/compare/v6.1.0...v6.7.0) --- updated-dependencies: - dependency-name: importlib-metadata dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: `sam logs` help text (#5397) * feat: `sam logs` help text * fix: make ruff happy * fix: address comments * feat: enable terraform support for local start-api command (#5389) * feat: Enable hook-name and skip-prepare-infra flagf for sam local start-api (#5217) * Enable hook-name flag for sam local start-api * Format files * test: Terraform local start-api integration tests base (#5240) * feat: update SAM CLI with latest App Templates commit hash (#5211) * feat: updating app templates repo hash with (a34f563f067e13df3eb350d36461b99397b6cda6) * dummy change to trigger checks * revert dummy commit --------- Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Enable hook-name flag for sam local start-api * Format files * fix: fix failing Terraform integration test cases (#5218) * fix: fix the failing terraform integration test cases * fix: fix the resource address while accessing the module config resources * fix: fix checking the experimental log integration test cases * chore: bump version to 1.85.0 (#5226) * chore: use the SAR Application created in testing accounts (#5221) * chore: update aws_lambda_builders to 1.32.0 (#5215) Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Added linking Gateway Method to Lambda Authorizer (#5228) * Added linking method to authorizer * Fixed docstring spelling mistake --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Return early during linking if no destination resources are found (#5220) * Returns during linking if no destination resources are found * Updated comment to correctly reflect state * Cleaned extra word --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * chore: Strengthen wording on "no Auth" during deploy (#5231) Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Link Lambda Authorizer to Rest API (#5219) * Link RestApiId property for Lambda Authorizers * Updated docstring * Format files --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Terraform start-api integration tests * Add test files * Uncomment skip --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Added OpenApi body integration testing and updated property builder (#5291) * Added OpenApi body integration testing and updated property builder * Added more test cases * Changed tearDown to tearDownClass * Updated JSON body parser to handle parsing errors and added unit tests * Removed V1 references * feat: Terraform Authorizer resource testing (#5270) * Added authorizer project * Added project files * Removed extra print * Add request based authorizer testing * test: Test the unsupported limitations for local start api (#5309) * test: Test the unsupported limitations for local start api * fix lint issues * apply pr comments * fix: Bug Bash UX Issues (#5387) * Fix bug bash UX issues * Fix beta warning printing extra characters * Fix authorizer logging --------- Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * Updated warning message about missing function in template (#5347) Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> * chore(deps-dev): bump types-pywin32 in /requirements (#5436) Bumps [types-pywin32](https://github.com/python/typeshed) from 306.0.0.0 to 306.0.0.2. - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-pywin32 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: Read APIGW V2 stage and integration resources * Format files --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> Co-authored-by: Haresh Nasit Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> * feat: Link v2 route to integration (#5444) * chore(deps): bump actions/setup-go from 3 to 4 (#5418) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * chore(deps-dev): bump filelock from 3.12.0 to 3.12.2 in /requirements (#5378) Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.12.0 to 3.12.2. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.12.0...3.12.2) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: updating app templates repo hash with (bb905c379830c3d8edbc196bda731076549028e3) (#5398) Co-authored-by: GitHub Action * fix: add a table for package help text. (#5298) * fix: add a table for package help text. * Update samcli/commands/package/core/command.py Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> * tests: fix strings in package help text * fix: PR comments * fix: PR comments. --------- Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> * fix: Handle BROKEN_PIPE_ERROR (#5386) * Handle pywintypes pipe exception * Moved exception checking to check for winerror * Use decorator and added unit tests * Added failure test case * make format * Added more context/comments * fix: remove circular dependency by moving parse_s3 method to its own util file (#5430) * fix: remove circular dependency by moving parse_s3 method to its own util file * add missing unit tests file * chore(deps): bump sympy from 1.10.1 to 1.12 in /requirements (#5338) Bumps [sympy](https://github.com/sympy/sympy) from 1.10.1 to 1.12. - [Release notes](https://github.com/sympy/sympy/releases) - [Commits](https://github.com/sympy/sympy/compare/sympy-1.10.1...sympy-1.12) --- updated-dependencies: - dependency-name: sympy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> * chore(deps): bump websocket-client from 1.5.1 to 1.6.1 in /requirements (#5417) Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.5.1 to 1.6.1. - [Release notes](https://github.com/websocket-client/websocket-client/releases) - [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog) - [Commits](https://github.com/websocket-client/websocket-client/compare/v1.5.1...v1.6.1) --- updated-dependencies: - dependency-name: websocket-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump ruamel-yaml from 0.17.21 to 0.17.32 in /requirements (#5376) * chore(deps): bump ruamel-yaml from 0.17.21 to 0.17.32 in /requirements Bumps [ruamel-yaml](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree) from 0.17.21 to 0.17.32. --- updated-dependencies: - dependency-name: ruamel-yaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Pin ruamel-yaml-clib version --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> Co-authored-by: Haresh Nasit * Updated package formatter to import package options instead of deploy (#5433) Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * chore(deps): bump importlib-metadata in /requirements (#5437) Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.1.0 to 6.7.0. - [Release notes](https://github.com/python/importlib_metadata/releases) - [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst) - [Commits](https://github.com/python/importlib_metadata/compare/v6.1.0...v6.7.0) --- updated-dependencies: - dependency-name: importlib-metadata dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: `sam logs` help text (#5397) * feat: `sam logs` help text * fix: make ruff happy * fix: address comments * feat: enable terraform support for local start-api command (#5389) * feat: Enable hook-name and skip-prepare-infra flagf for sam local start-api (#5217) * Enable hook-name flag for sam local start-api * Format files * test: Terraform local start-api integration tests base (#5240) * feat: update SAM CLI with latest App Templates commit hash (#5211) * feat: updating app templates repo hash with (a34f563f067e13df3eb350d36461b99397b6cda6) * dummy change to trigger checks * revert dummy commit --------- Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Enable hook-name flag for sam local start-api * Format files * fix: fix failing Terraform integration test cases (#5218) * fix: fix the failing terraform integration test cases * fix: fix the resource address while accessing the module config resources * fix: fix checking the experimental log integration test cases * chore: bump version to 1.85.0 (#5226) * chore: use the SAR Application created in testing accounts (#5221) * chore: update aws_lambda_builders to 1.32.0 (#5215) Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Added linking Gateway Method to Lambda Authorizer (#5228) * Added linking method to authorizer * Fixed docstring spelling mistake --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Return early during linking if no destination resources are found (#5220) * Returns during linking if no destination resources are found * Updated comment to correctly reflect state * Cleaned extra word --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * chore: Strengthen wording on "no Auth" during deploy (#5231) Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Link Lambda Authorizer to Rest API (#5219) * Link RestApiId property for Lambda Authorizers * Updated docstring * Format files --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * Terraform start-api integration tests * Add test files * Uncomment skip --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * feat: Added OpenApi body integration testing and updated property builder (#5291) * Added OpenApi body integration testing and updated property builder * Added more test cases * Changed tearDown to tearDownClass * Updated JSON body parser to handle parsing errors and added unit tests * Removed V1 references * feat: Terraform Authorizer resource testing (#5270) * Added authorizer project * Added project files * Removed extra print * Add request based authorizer testing * test: Test the unsupported limitations for local start api (#5309) * test: Test the unsupported limitations for local start api * fix lint issues * apply pr comments * fix: Bug Bash UX Issues (#5387) * Fix bug bash UX issues * Fix beta warning printing extra characters * Fix authorizer logging --------- Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> * Updated warning message about missing function in template (#5347) Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> * chore(deps-dev): bump types-pywin32 in /requirements (#5436) Bumps [types-pywin32](https://github.com/python/typeshed) from 306.0.0.0 to 306.0.0.2. - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-pywin32 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: Read APIGW V2 stage and integration resources * Format files * feat: Link gateway v2 routes to gateway v2 integrations * Add unit testing * Fix linter error --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> Co-authored-by: Haresh Nasit Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> * chore: fix the v2 gateway integration to route resources linking (#5478) * feat: Link v2 integration to lambda function (#5481) * link aws_apigatewayv2_integration and aws_lambda_function * feat: Link v2 integration to lambda function * fix merge error * run make format * feat: Link v2 integration to V2 API (#5486) * feat: Link v2 integration to V2 API * run make format * feat: Link V2 Route to V2 API (#5503) * feat: Link gateway v2 routes to gateway v2 apis * Format files --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> * feat: Link V2 Stages to V2 APIs (#5622) * feat: Link gateway v2 stages to gateway v2 apis * Rename test * feat: Link Gateway V2 APIs to Lambda Functions (#5626) * feat: Link gateway v2 apis to lambda functions * Fix typo * Format files * feat: Add APIGW V2 Lambda Authorizer mapping and linking (#5625) * Added V2 Authorizer property mapping * make format * Link a V2 Authorizer to a Lambda Function * Link V2 Authorizers to V2 Api * Added missing lines to include new resource in testing suite * Removed max diff pytest option * test: Add terraform v2 api integration test cases (#5634) * chore: merge from develop branch, and apply the linking refactor (#5701) * fix(invoke): Write in UTF-8 string instead of bytes (#5642) * Revert "fix: Revert UTF-8 fixes #5485 and #5427 (#5512)" This reverts commit 36f8bf970df7a8072eef58ca76405c40e1cc1c90. * Enforce utf8 on stdout/stderr/logfile --------- Co-authored-by: Jacob Fuss * chore(deps): bump cryptography from 41.0.2 to 41.0.3 in /requirements (#5675) Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.2 to 41.0.3. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/41.0.2...41.0.3) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(test): Force reading file with utf8 in tests for windows (#5679) Co-authored-by: Jacob Fuss * fix(test): Increase max execution time for timeout tests (#5680) Co-authored-by: Jacob Fuss * chore: auto update reproducible requirements when there is a dependency change (#5677) * chore: add reproducible gha * format * formatting v2 * format v3 * use win make target * update windows job * update win paths * why windows why * remove if stmt for now * test with direct paths * update windows paths again * bring back activate * add dummy win file and push changes * update base.txt for testing * Update reproducibles * run jobs sequentially * print changed flag * check changes again * run git status before moving forward * refresh index before checking any changed files * Update reproducibles * commit or skip * Update reproducibles * rerun build & pyinstaller jobs once update reproducible finishes * add more details to commit message * nuke all reproducibles to trigger the CI * Update reproducibles: update-reproducible-linux-reqs * Update reproducibles: update-reproducible-mac-reqs * Update reproducibles: update-reproducible-win-reqs * change lb version for testing * update permissions and limit only for aws/aws-sam-cli * Update reproducibles: update-reproducible-linux-reqs * Update reproducibles: update-reproducible-mac-reqs * Update reproducibles: update-reproducible-win-reqs * update other jobs as well * run without file filter * Update reproducibles: update-reproducible-linux-reqs * Update reproducibles: update-reproducible-mac-reqs * Update reproducibles: update-reproducible-win-reqs * put the file filter back --------- Co-authored-by: GitHub Action * fix: Repair failing integration test (#5698) * Fix failing integration test * Be more specific with error message --------- Co-authored-by: Leonardo Gama * fix: link the API gateway resource parent to either rest api or another gateway resource (#5697) * chore: merge from develop branch, and apply the linking refactor * fix merge mistake in the integration test cases --------- Signed-off-by: dependabot[bot] Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Leonardo Gama <51037424+Leo10Gama@users.noreply.github.com> Co-authored-by: Leonardo Gama * tests: Added limitation test cases for V2 resources (#5708) * chore: merge from develop branch, and apply the linking refactor * Added limitation test cases for V2 resources * Removed commented out code * fix merge mistake in the integration test cases * Updated and added happy paths to apply test class * Use correct method for AWS_PROXY * fix failing integration testing * fix merge conflicts --------- Co-authored-by: Mohamed ElAsmar * feat: Link V2 Routes to Authorizer (#5732) * feat: Link V2 Routes to Authorizer * adding authorizers integration testing * feat: remove beta-features flag usage for terraform feature (#5760) * chore: Resolve conflicts with upstream/develop (#5830) * feat: Added environment variable validation for Terraform arguments (#5809) * Added environment variable validation * Added integration tests * Added typing and more detailed doc string * feat: Report TF custom plan file usage in telemetry (#5825) * feat: Report TF custom plan file usage in telemetry * Read property from config * Fix sam remote invoke to take profile properly (#5823) * Fix sam remote invoke to take profile properly * error handling --------- Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Co-authored-by: Daniel Mil <84205762+mildaniel@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: GitHub Action Co-authored-by: Lucas <12496191+lucashuy@users.noreply.github.com> Co-authored-by: Jacob Fuss <32497805+jfuss@users.noreply.github.com> Co-authored-by: Jacob Fuss Co-authored-by: Sriram Madapusi Vasudevan <3770774+sriram-mv@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Daniel Mil Co-authored-by: Mehmet Nuri Deveci <5735811+mndeveci@users.noreply.github.com> Co-authored-by: hnnasit <84355507+hnnasit@users.noreply.github.com> Co-authored-by: Haresh Nasit Co-authored-by: Wing Fung Lau <4760060+hawflau@users.noreply.github.com> Co-authored-by: Leonardo Gama <51037424+Leo10Gama@users.noreply.github.com> Co-authored-by: Leonardo Gama --- .../_utils/custom_options/hook_name_option.py | 83 --- samcli/commands/build/command.py | 8 - samcli/commands/local/invoke/cli.py | 9 - samcli/commands/local/start_api/cli.py | 9 - samcli/commands/local/start_lambda/cli.py | 9 - .../terraform/hooks/prepare/constants.py | 5 + .../terraform/hooks/prepare/exceptions.py | 120 ++++ .../hooks/prepare/property_builder.py | 118 ++++ .../hooks/prepare/resource_linking.py | 623 +++++++++++++++++ .../hooks/prepare/resources/apigw.py | 45 ++ .../hooks/prepare/resources/resource_links.py | 59 ++ .../prepare/resources/resource_properties.py | 15 + .../terraform/hooks/prepare/translate.py | 2 + .../test_build_terraform_applications.py | 16 - ...uild_terraform_applications_other_cases.py | 102 +-- tests/integration/local/common_utils.py | 6 +- .../test_invoke_terraform_applications.py | 196 +----- ...st_start_api_with_terraform_application.py | 131 +++- ...est_start_lambda_terraform_applications.py | 152 ----- .../root_module/input_samconfig.yaml | 1 - .../terraform/lambda-auth-openapi/main.tf | 17 +- .../terraform/terraform-v1-api-simple/main.tf | 13 +- .../HelloWorldFunction.zip | Bin 0 -> 1079 bytes .../terraform-v2-api-quick-create/main.tf | 57 ++ .../HelloWorldFunction.zip | Bin 0 -> 1079 bytes .../main.tf | 86 +++ .../HelloWorldFunction.zip | Bin 0 -> 1079 bytes .../main.tf | 99 +++ .../HelloWorldFunction.zip | Bin 0 -> 1079 bytes .../terraform/terraform-v2-api-simple/main.tf | 82 +++ .../lambda-functions.zip | Bin 0 -> 3146 bytes .../terraform-v2-auth-openapi/main.tf | 118 ++++ .../terraform-v2-openapi/lambda-functions.zip | Bin 0 -> 1079 bytes .../terraform/terraform-v2-openapi/main.tf | 62 ++ .../terraform/v1-lambda-authorizer/main.tf | 48 +- .../v2-lambda-authorizer/lambda-functions.zip | Bin 0 -> 3146 bytes .../terraform/v2-lambda-authorizer/main.tf | 111 +++ .../test_hook_package_id_option.py | 303 +-------- tests/unit/commands/buildcmd/test_command.py | 34 - .../terraform/hooks/prepare/prepare_base.py | 233 ++++++- .../hooks/prepare/test_property_builder.py | 33 + .../hooks/prepare/test_resource_linking.py | 631 ++++++++++++++++++ .../terraform/hooks/prepare/test_translate.py | 41 ++ 43 files changed, 2696 insertions(+), 981 deletions(-) create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/HelloWorldFunction.zip create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/main.tf create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/HelloWorldFunction.zip create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/main.tf create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/HelloWorldFunction.zip create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/main.tf create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/HelloWorldFunction.zip create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/main.tf create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/lambda-functions.zip create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/main.tf create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-openapi/lambda-functions.zip create mode 100644 tests/integration/testdata/start_api/terraform/terraform-v2-openapi/main.tf create mode 100644 tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/lambda-functions.zip create mode 100644 tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/main.tf diff --git a/samcli/commands/_utils/custom_options/hook_name_option.py b/samcli/commands/_utils/custom_options/hook_name_option.py index 87888d63a1..94f89445e2 100644 --- a/samcli/commands/_utils/custom_options/hook_name_option.py +++ b/samcli/commands/_utils/custom_options/hook_name_option.py @@ -9,14 +9,7 @@ import click from samcli.cli.context import Context -from samcli.cli.global_config import GlobalConfig from samcli.commands._utils.constants import DEFAULT_BUILT_TEMPLATE_PATH -from samcli.commands._utils.experimental import ( - ExperimentalFlag, - prompt_experimental, - set_experimental, - update_experimental_context, -) from samcli.lib.hook.exceptions import InvalidHookWrapperException from samcli.lib.hook.hook_wrapper import IacHookWrapper, get_available_hook_packages_ids from samcli.lib.telemetry.event import EventName, EventTracker @@ -67,9 +60,6 @@ def handle_parse_result(self, ctx, opts, args): _validate_build_command_parameters(command_name, opts) - if not _check_experimental_flag(hook_name, command_name, opts, ctx.default_map): - return super().handle_parse_result(ctx, opts, args) - try: self._call_prepare_hook(iac_hook_wrapper, opts, ctx) except Exception as ex: @@ -149,79 +139,6 @@ def _validate_build_command_parameters(command_name, opts): ) -def _check_experimental_flag(hook_name, command_name, opts, default_map): - # check beta-feature - experimental_entry = ExperimentalFlag.IaCsSupport.get(hook_name) - beta_features = _get_customer_input_beta_features_option(default_map, experimental_entry, opts) - - # check if beta feature flag is required for a specific hook package - # The IaCs support experimental flag map will contain only the beta IaCs. In case we support the external - # hooks, we need to first know that the hook package is an external, and to handle the beta feature of it - # using different approach - if beta_features is None and experimental_entry is not None: - iac_support_message = _get_iac_support_experimental_prompt_message(hook_name, command_name) - if not prompt_experimental(experimental_entry, iac_support_message): - LOG.debug("Experimental flag is disabled and prepare hook is not run") - return False - elif not beta_features: - LOG.debug("--beta-features flag is disabled and prepare hook is not run") - return False - elif beta_features: - LOG.debug("--beta-features flag is enabled, enabling experimental flag.") - set_experimental(experimental_entry, True) - update_experimental_context() - return True - - -def _get_customer_input_beta_features_option(default_map, experimental_entry, opts): - # Get the beta-features flag value from the command parameters if provided. - beta_features = opts.get("beta_features") - if beta_features is not None: - return beta_features - - # Get the beta-features flag value from the SamConfig file if provided. - beta_features = default_map.get("beta_features") - if beta_features is not None: - return beta_features - - # Get the beta-features flag value from the environment variables. - if experimental_entry: - gc = GlobalConfig() - beta_features = gc.get_value(experimental_entry, default=None, value_type=bool, is_flag=True) - if beta_features is not None: - return beta_features - return gc.get_value(ExperimentalFlag.All, default=None, value_type=bool, is_flag=True) - - return None - - -def _get_iac_support_experimental_prompt_message(hook_name: str, command: str) -> str: - """ - return the customer prompt message for a specific hook package. - - Parameters - ---------- - hook_name: str - the hook name to determine what is the supported iac - - command: str - the current sam command - Returns - ------- - str - the customer prompt message for a specific IaC. - """ - - supported_iacs_messages = { - "terraform": ( - "Supporting Terraform applications is a beta feature.\n" - "Please confirm if you would like to proceed using AWS SAM CLI with terraform application.\n" - f"You can also enable this beta feature with 'sam {command} --beta-features'." - ) - } - return supported_iacs_messages.get(hook_name, "") - - def _read_parameter_value(param_name, opts, ctx, default_value=None): """ Read SAM CLI parameter value either from the parameters list or from the samconfig values diff --git a/samcli/commands/build/command.py b/samcli/commands/build/command.py index 1eab7c585e..a7fb6bad9d 100644 --- a/samcli/commands/build/command.py +++ b/samcli/commands/build/command.py @@ -8,7 +8,6 @@ import click from samcli.cli.context import Context -from samcli.commands._utils.experimental import ExperimentalFlag, is_experimental_enabled from samcli.commands._utils.options import ( skip_prepare_infra_option, template_option_without_build, @@ -220,13 +219,6 @@ def do_cli( # pylint: disable=too-many-locals, too-many-statements """ Implementation of the ``cli`` method """ - if ( - hook_name - and ExperimentalFlag.IaCsSupport.get(hook_name) is not None - and not is_experimental_enabled(ExperimentalFlag.IaCsSupport[hook_name]) - ): - LOG.info("Terraform Support beta feature is not enabled.") - return from samcli.commands.build.build_context import BuildContext diff --git a/samcli/commands/local/invoke/cli.py b/samcli/commands/local/invoke/cli.py index f8e87f4e27..bb5de0616c 100644 --- a/samcli/commands/local/invoke/cli.py +++ b/samcli/commands/local/invoke/cli.py @@ -9,7 +9,6 @@ from samcli.cli.cli_config_file import ConfigProvider, configuration_option from samcli.cli.main import aws_creds_options, pass_context, print_cmdline_args from samcli.cli.main import common_options as cli_framework_options -from samcli.commands._utils.experimental import ExperimentalFlag, is_experimental_enabled from samcli.commands._utils.option_value_processor import process_image_options from samcli.commands._utils.options import hook_name_click_option, skip_prepare_infra_option, terraform_plan_file_option from samcli.commands.local.cli_common.options import invoke_common_options, local_common_options @@ -162,14 +161,6 @@ def do_cli( # pylint: disable=R0914 from samcli.local.docker.manager import DockerImagePullFailedException from samcli.local.lambdafn.exceptions import FunctionNotFound - if ( - hook_name - and ExperimentalFlag.IaCsSupport.get(hook_name) is not None - and not is_experimental_enabled(ExperimentalFlag.IaCsSupport.get(hook_name)) - ): - LOG.info("Terraform Support beta feature is not enabled.") - return - LOG.debug("local invoke command is called") if event: diff --git a/samcli/commands/local/start_api/cli.py b/samcli/commands/local/start_api/cli.py index 9d3247eeef..9ba18564f5 100644 --- a/samcli/commands/local/start_api/cli.py +++ b/samcli/commands/local/start_api/cli.py @@ -9,7 +9,6 @@ from samcli.cli.cli_config_file import ConfigProvider, configuration_option from samcli.cli.main import aws_creds_options, pass_context, print_cmdline_args from samcli.cli.main import common_options as cli_framework_options -from samcli.commands._utils.experimental import ExperimentalFlag, is_experimental_enabled from samcli.commands._utils.option_value_processor import process_image_options from samcli.commands._utils.options import ( generate_next_command_recommendation, @@ -186,14 +185,6 @@ def do_cli( # pylint: disable=R0914 LOG.debug("local start-api command is called") - if ( - hook_name - and ExperimentalFlag.IaCsSupport.get(hook_name) is not None - and not is_experimental_enabled(ExperimentalFlag.IaCsSupport.get(hook_name)) - ): - LOG.info("Terraform Support beta feature is not enabled.") - return - processed_invoke_images = process_image_options(invoke_image) # Pass all inputs to setup necessary context to invoke function locally. diff --git a/samcli/commands/local/start_lambda/cli.py b/samcli/commands/local/start_lambda/cli.py index 8dd78dc2f9..75184a4e58 100644 --- a/samcli/commands/local/start_lambda/cli.py +++ b/samcli/commands/local/start_lambda/cli.py @@ -9,7 +9,6 @@ from samcli.cli.cli_config_file import ConfigProvider, configuration_option from samcli.cli.main import aws_creds_options, pass_context, print_cmdline_args from samcli.cli.main import common_options as cli_framework_options -from samcli.commands._utils.experimental import ExperimentalFlag, is_experimental_enabled from samcli.commands._utils.option_value_processor import process_image_options from samcli.commands._utils.options import ( generate_next_command_recommendation, @@ -169,14 +168,6 @@ def do_cli( # pylint: disable=R0914 from samcli.lib.providers.exceptions import InvalidLayerReference from samcli.local.docker.lambda_debug_settings import DebuggingNotSupported - if ( - hook_name - and ExperimentalFlag.IaCsSupport.get(hook_name) is not None - and not is_experimental_enabled(ExperimentalFlag.IaCsSupport.get(hook_name)) - ): - LOG.info("Terraform Support beta feature is not enabled.") - return - LOG.debug("local start_lambda command is called") processed_invoke_images = process_image_options(invoke_image) diff --git a/samcli/hook_packages/terraform/hooks/prepare/constants.py b/samcli/hook_packages/terraform/hooks/prepare/constants.py index ffde46e57c..e6ea7a1fca 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/constants.py +++ b/samcli/hook_packages/terraform/hooks/prepare/constants.py @@ -23,3 +23,8 @@ TF_AWS_API_GATEWAY_INTEGRATION = "aws_api_gateway_integration" TF_AWS_API_GATEWAY_AUTHORIZER = "aws_api_gateway_authorizer" TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE = "aws_api_gateway_method_response" +TF_AWS_API_GATEWAY_V2_API = "aws_apigatewayv2_api" +TF_AWS_API_GATEWAY_V2_ROUTE = "aws_apigatewayv2_route" +TF_AWS_API_GATEWAY_V2_STAGE = "aws_apigatewayv2_stage" +TF_AWS_API_GATEWAY_V2_INTEGRATION = "aws_apigatewayv2_integration" +TF_AWS_API_GATEWAY_V2_AUTHORIZER = "aws_apigatewayv2_authorizer" diff --git a/samcli/hook_packages/terraform/hooks/prepare/exceptions.py b/samcli/hook_packages/terraform/hooks/prepare/exceptions.py index f0ea430478..3a8d9c2b67 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/exceptions.py +++ b/samcli/hook_packages/terraform/hooks/prepare/exceptions.py @@ -254,6 +254,126 @@ class GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException( """ +class OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Route linking to more than one Gateway V2 Integration + """ + + +class GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException( + LocalVariablesLinkingLimitationException +): + """ + Exception specific for Gateway V2 Route linking to Gateway V2 Integration using locals. + """ + + +class OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Integration linking to more than one Lambda function + """ + + +class GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException( + LocalVariablesLinkingLimitationException +): + """ + Exception specific for Gateway V2 Integration linking to Lambda Function using locals. + """ + + +class OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Integration linking to more than one Gateway V2 API + """ + + +class GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException( + LocalVariablesLinkingLimitationException +): + """ + Exception specific for Gateway V2 Integration linking to Gateway V2 API using locals. + """ + + +class OneGatewayV2RouteToGatewayV2ApiLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Route linking to more than one Gateway V2 API + """ + + +class GatewayV2RouteToGatewayV2ApiLocalVariablesLinkingLimitationException(LocalVariablesLinkingLimitationException): + """ + Exception specific for Gateway V2 Route linking to Gateway V2 API using locals. + """ + + +class OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Authorizer linking to more than one Lambda Function + """ + + +class GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException( + LocalVariablesLinkingLimitationException +): + """ + Exception specific for Gateway V2 Authorizer linking to Lambda Function using locals. + """ + + +class OneGatewayV2AuthorizerToGatewayV2ApiLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Authorizer linking to more than one Gateway V2 API + """ + + +class GatewayV2AuthorizerToGatewayV2ApiLocalVariablesLinkingLimitationException( + LocalVariablesLinkingLimitationException +): + """ + Exception specific for Gateway V2 Authorizer linking to Gateway V2 API using locals. + """ + + +class OneGatewayV2ApiToLambdaFunctionLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 API linking to more than one Lambda Function + """ + + +class GatewayV2ApiToLambdaFunctionLocalVariablesLinkingLimitationException(LocalVariablesLinkingLimitationException): + """ + Exception specific for Gateway V2 API linking to Lambda Function using locals. + """ + + +class OneGatewayV2StageToGatewayV2ApiLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Stage linking to more than one Gateway V2 API + """ + + +class GatewayV2StageToGatewayV2ApiLocalVariablesLinkingLimitationException(LocalVariablesLinkingLimitationException): + """ + Exception specific for Gateway V2 Stage linking to Gateway V2 API using locals. + """ + + +class OneGatewayV2RouteToGatewayV2AuthorizerLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway V2 Route linking to more than one Gateway V2 Authorizer + """ + + +class GatewayV2RouteToGatewayV2AuthorizerLocalVariablesLinkingLimitationException( + LocalVariablesLinkingLimitationException +): + """ + Exception specific for Gateway V2 Route linking to Gateway V2 Authorizer using locals. + """ + + class InvalidSamMetadataPropertiesException(UserException): pass diff --git a/samcli/hook_packages/terraform/hooks/prepare/property_builder.py b/samcli/hook_packages/terraform/hooks/prepare/property_builder.py index 4fe50e935a..efb88bedf3 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/property_builder.py +++ b/samcli/hook_packages/terraform/hooks/prepare/property_builder.py @@ -15,6 +15,11 @@ TF_AWS_API_GATEWAY_RESOURCE, TF_AWS_API_GATEWAY_REST_API, TF_AWS_API_GATEWAY_STAGE, + TF_AWS_API_GATEWAY_V2_API, + TF_AWS_API_GATEWAY_V2_AUTHORIZER, + TF_AWS_API_GATEWAY_V2_INTEGRATION, + TF_AWS_API_GATEWAY_V2_ROUTE, + TF_AWS_API_GATEWAY_V2_STAGE, TF_AWS_LAMBDA_FUNCTION, TF_AWS_LAMBDA_LAYER_VERSION, ) @@ -36,6 +41,11 @@ from samcli.lib.utils.resources import AWS_APIGATEWAY_RESOURCE as CFN_AWS_APIGATEWAY_RESOURCE from samcli.lib.utils.resources import AWS_APIGATEWAY_RESTAPI as CFN_AWS_APIGATEWAY_RESTAPI from samcli.lib.utils.resources import AWS_APIGATEWAY_STAGE as CFN_AWS_APIGATEWAY_STAGE +from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_API as CFN_AWS_APIGATEWAY_V2_API +from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_AUTHORIZER as CFN_AWS_APIGATEWAY_V2_AUTHORIZER +from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_INTEGRATION as CFN_AWS_APIGATEWAY_V2_INTEGRATION +from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_ROUTE as CFN_AWS_APIGATEWAY_V2_ROUTE +from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_STAGE as CFN_AWS_APIGATEWAY_V2_STAGE from samcli.lib.utils.resources import AWS_LAMBDA_FUNCTION as CFN_AWS_LAMBDA_FUNCTION from samcli.lib.utils.resources import AWS_LAMBDA_LAYERVERSION as CFN_AWS_LAMBDA_LAYER_VERSION @@ -245,6 +255,59 @@ def _get_json_body(tf_properties: dict, resource: TFResource) -> Any: return body +def _get_cors_v2_api(tf_properties: dict, resource: TFResource) -> Optional[Dict[str, Any]]: + """ + Extract the cors properties from an aws_apigatewayv2_api since they are in a nested property + called "cors_configuration" and not in the root of the resource + + e.g. + resource "aws_apigatewayv2_api" "my_api" { + name = "my_api" + protocol_type = "HTTP" + + cors_configuration { + allow_credentials = true + allow_headers = ["Content-Type"] + allow_methods = ["OPTIONS", "POST", "GET"] + allow_origins = ["my-origin.com"] + max_age = "500" + } + } + + Parameters + ---------- + tf_properties: dict + Properties of the terraform AWS Lambda function resource + resource: TFResource + Configuration terraform resource + + Returns + ------- + Optional[Dict[str, Any]] + Optional dictionary containing the extracted cors properties with CFN property names + """ + cors = tf_properties.get("cors_configuration") + if not cors: + return None + + extractable_configs = cors[0] + cors_configuration = {} + + def _add_property(cfn_prop, tf_prop): + property_value = extractable_configs.get(tf_prop) + if property_value: + cors_configuration[cfn_prop] = property_value + + _add_property("AllowCredentials", "allow_credentials") + _add_property("AllowHeaders", "allow_headers") + _add_property("AllowMethods", "allow_methods") + _add_property("AllowOrigins", "allow_origins") + _add_property("ExposeHeaders", "expose_headers") + _add_property("MaxAge", "max_age") + + return cors_configuration + + AWS_LAMBDA_FUNCTION_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { "FunctionName": _get_property_extractor("function_name"), "Architectures": _get_property_extractor("architectures"), @@ -320,6 +383,46 @@ def _get_json_body(tf_properties: dict, resource: TFResource) -> Any: "ResponseParameters": _get_property_extractor("response_parameters"), } +AWS_API_GATEWAY_V2_API_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { + "Name": _get_property_extractor("name"), + "Body": _get_json_body, + "Target": _get_property_extractor("target"), + "ProtocolType": _get_property_extractor("protocol_type"), + "RouteKey": _get_property_extractor("route_key"), + "CorsConfiguration": _get_cors_v2_api, +} + +AWS_API_GATEWAY_V2_ROUTE_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { + "ApiId": _get_property_extractor("api_id"), + "RouteKey": _get_property_extractor("route_key"), + "Target": _get_property_extractor("target"), + "OperationName": _get_property_extractor("operation_name"), +} + +AWS_API_GATEWAY_V2_STAGE_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { + "ApiId": _get_property_extractor("api_id"), + "StageName": _get_property_extractor("name"), + "StageVariables": _get_property_extractor("stage_variables"), +} + +AWS_API_GATEWAY_V2_INTEGRATION_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { + "ApiId": _get_property_extractor("api_id"), + "IntegrationType": _get_property_extractor("integration_type"), + "IntegrationMethod": _get_property_extractor("integration_method"), + "IntegrationUri": _get_property_extractor("integration_uri"), + "PayloadFormatVersion": _get_property_extractor("payload_format_version"), +} + +AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = { + "ApiId": _get_property_extractor("api_id"), + "AuthorizerType": _get_property_extractor("authorizer_type"), + "AuthorizerUri": _get_property_extractor("authorizer_uri"), + "Name": _get_property_extractor("name"), + "AuthorizerPayloadFormatVersion": _get_property_extractor("authorizer_payload_format_version"), + "IdentitySource": _get_property_extractor("identity_sources"), + "EnableSimpleResponses": _get_property_extractor("enable_simple_responses"), +} + RESOURCE_TRANSLATOR_MAPPING: Dict[str, ResourceTranslator] = { TF_AWS_LAMBDA_FUNCTION: ResourceTranslator(CFN_AWS_LAMBDA_FUNCTION, AWS_LAMBDA_FUNCTION_PROPERTY_BUILDER_MAPPING), TF_AWS_LAMBDA_LAYER_VERSION: ResourceTranslator( @@ -346,4 +449,19 @@ def _get_json_body(tf_properties: dict, resource: TFResource) -> Any: TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE: ResourceTranslator( INTERNAL_API_GATEWAY_INTEGRATION_RESPONSE, AWS_API_GATEWAY_INTEGRATION_RESPONSE_PROPERTY_BUILDER_MAPPING ), + TF_AWS_API_GATEWAY_V2_API: ResourceTranslator( + CFN_AWS_APIGATEWAY_V2_API, AWS_API_GATEWAY_V2_API_PROPERTY_BUILDER_MAPPING + ), + TF_AWS_API_GATEWAY_V2_ROUTE: ResourceTranslator( + CFN_AWS_APIGATEWAY_V2_ROUTE, AWS_API_GATEWAY_V2_ROUTE_PROPERTY_BUILDER_MAPPING + ), + TF_AWS_API_GATEWAY_V2_STAGE: ResourceTranslator( + CFN_AWS_APIGATEWAY_V2_STAGE, AWS_API_GATEWAY_V2_STAGE_PROPERTY_BUILDER_MAPPING + ), + TF_AWS_API_GATEWAY_V2_INTEGRATION: ResourceTranslator( + CFN_AWS_APIGATEWAY_V2_INTEGRATION, AWS_API_GATEWAY_V2_INTEGRATION_PROPERTY_BUILDER_MAPPING + ), + TF_AWS_API_GATEWAY_V2_AUTHORIZER: ResourceTranslator( + CFN_AWS_APIGATEWAY_V2_AUTHORIZER, AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING + ), } diff --git a/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py b/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py index 5c9bba5a26..a7fbadd4e2 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py @@ -18,6 +18,15 @@ GatewayResourceToApiGatewayMethodLocalVariablesLinkingLimitationException, GatewayResourceToGatewayRestApiLocalVariablesLinkingLimitationException, GatewayResourceToParentResourceLocalVariablesLinkingLimitationException, + GatewayV2ApiToLambdaFunctionLocalVariablesLinkingLimitationException, + GatewayV2AuthorizerToGatewayV2ApiLocalVariablesLinkingLimitationException, + GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, + GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException, + GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException, + GatewayV2RouteToGatewayV2ApiLocalVariablesLinkingLimitationException, + GatewayV2RouteToGatewayV2AuthorizerLocalVariablesLinkingLimitationException, + GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException, + GatewayV2StageToGatewayV2ApiLocalVariablesLinkingLimitationException, InvalidResourceLinkingException, LambdaFunctionToApiGatewayIntegrationLocalVariablesLinkingLimitationException, LocalVariablesLinkingLimitationException, @@ -29,6 +38,15 @@ OneGatewayResourceToApiGatewayMethodLinkingLimitationException, OneGatewayResourceToParentResourceLinkingLimitationException, OneGatewayResourceToRestApiLinkingLimitationException, + OneGatewayV2ApiToLambdaFunctionLinkingLimitationException, + OneGatewayV2AuthorizerToGatewayV2ApiLinkingLimitationException, + OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException, + OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException, + OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException, + OneGatewayV2RouteToGatewayV2ApiLinkingLimitationException, + OneGatewayV2RouteToGatewayV2AuthorizerLinkingLimitationException, + OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException, + OneGatewayV2StageToGatewayV2ApiLinkingLimitationException, OneLambdaFunctionResourceToApiGatewayIntegrationLinkingLimitationException, OneLambdaLayerLinkingLimitationException, OneResourceLinkingLimitationException, @@ -59,12 +77,33 @@ API_GATEWAY_REST_API_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_rest_api." API_GATEWAY_RESOURCE_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_resource." API_GATEWAY_AUTHORIZER_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_authorizer." +API_GATEWAY_V2_INTEGRATION_RESOURCE_ADDRESS_PREFIX = "aws_apigatewayv2_integration." +API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX = "aws_apigatewayv2_api." +API_GATEWAY_V2_AUTHORIZER_RESOURCE_ADDRESS_PREFIX = "aws_apigatewayv2_authorizer." TERRAFORM_LOCAL_VARIABLES_ADDRESS_PREFIX = "local." DATA_RESOURCE_ADDRESS_PREFIX = "data." LOG = logging.getLogger(__name__) +def _default_tf_destination_value_id_extractor(value: str) -> str: + """ + The default function to extract the Terraform destination resource id from the linking property value. The logic of + this function is to return the same input value. + + Parameters + ---------- + value: str + linking property value + + Returns + -------- + str: + the extracted destination resource value. + """ + return value + + @dataclass class ReferenceType: """ @@ -117,6 +156,10 @@ class ResourceLinkingPair: cfn_link_field_name: str cfn_resource_update_call_back_function: Callable[[Dict, List[ReferenceType]], None] linking_exceptions: ResourcePairExceptions + # function to extract the terraform destination value from the linking field value + tf_destination_value_extractor_from_link_field_value_function: Callable[ + [str], str + ] = _default_tf_destination_value_id_extractor class ResourceLinker: @@ -281,6 +324,12 @@ def _link_using_linking_fields(self, cfn_resource: Dict) -> None: if not isinstance(values, List): values = [values] + # loop on the linking field values and call the Logical Id extractor function to extrac the destination resource + # logical id. + values = [ + self._resource_pair.tf_destination_value_extractor_from_link_field_value_function(value) for value in values + ] + # build map between the destination linking field property values, and resources' logical ids expected_destinations_map = { expected_destination.terraform_resource_type_prefix: expected_destination.terraform_attribute_name @@ -1819,3 +1868,577 @@ def _link_gateway_method_to_gateway_authorizer( linking_exceptions=exceptions, ) ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_route_to_integration_callback( + gateway_v2_route_cfn_resource: Dict, gateway_v2_integration_resources: List[ReferenceType] +): + """ + Callback function that is used by the linking algorithm to update a CFN V2 Route Resource with + a reference to the Gateway V2 integration resource + + Parameters + ---------- + gateway_v2_route_cfn_resource: Dict + API Gateway V2 Route CFN resource + gateway_v2_integration_resources: List[ReferenceType] + List of referenced Gateway V2 Integrations either as the logical id of Integration resources + defined in the customer project, or ARN values for actual Integration defined + in customer's account. This list should always contain one element only. + """ + if len(gateway_v2_integration_resources) > 1: + raise InvalidResourceLinkingException("Could not link multiple Gateway V2 Integrations to one Gateway V2 Route") + + if not gateway_v2_integration_resources: + LOG.info( + "Unable to find any references to Gateway V2 Integrations, " + "skip linking Gateway V2 Route to Gateway V2 Integrations" + ) + return + + logical_id = gateway_v2_integration_resources[0] + if isinstance(logical_id, LogicalIdReference): + gateway_v2_route_cfn_resource["Properties"]["Target"] = { + "Fn::Join": ["/", ["integrations", {"Ref": logical_id.value}]] + } + elif not logical_id.value.startswith("integrations/"): + gateway_v2_route_cfn_resource["Properties"]["Target"] = f"integrations/{logical_id.value}" + else: + gateway_v2_route_cfn_resource["Properties"]["Target"] = logical_id.value + + +def _extract_gateway_v2_integration_id_from_route_target_value(value: str) -> str: + """ + Function to extract the Gateway V2 Integration id value from the Gateway V2 Route resource target property value. + The Route Target value should be in the format `integrations/` + + Parameters + ---------- + value: str + Gateway V2 Route target property value + + Returns + -------- + str: + The Gateway V2 integration id extracted from the route target property + """ + if value.startswith("integrations/"): + return value[len("integrations/") :] + return value + + +def _link_gateway_v2_route_to_integration( + gateway_route_config_resources: Dict[str, TFResource], + gateway_route_config_address_cfn_resources_map: Dict[str, List], + integration_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding + Gateway V2 Route resources to each Gateway V2 Integration + + Parameters + ---------- + gateway_route_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway Routes + gateway_route_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway Route + integration_resources: Dict[str, Dict] + Dictionary of all Terraform Gateway V2 integration resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException, + local_variable_linking_exception=GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=gateway_route_config_address_cfn_resources_map, + source_resource_tf_config=gateway_route_config_resources, + destination_resource_tf=integration_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_INTEGRATION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="target", + cfn_link_field_name="Target", + cfn_resource_update_call_back_function=_link_gateway_v2_route_to_integration_callback, + linking_exceptions=exceptions, + tf_destination_value_extractor_from_link_field_value_function=_extract_gateway_v2_integration_id_from_route_target_value, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_integration_to_lambda_function_callback( + gateway_v2_integration_cfn_resource: Dict, lambda_function_resources: List[ReferenceType] +): + """ + Callback function that is used by the linking algorithm to update a CFN V2 Route Resource with a reference to + the Gateway V2 integration resource + + Parameters + ---------- + gateway_v2_integration_cfn_resource: Dict + API Gateway V2 Integration CFN resource + lambda_function_resources: List[ReferenceType] + List of referenced lambda function either as the logical id of Integration resources + defined in the customer project, or the invocation ARN values for actual functions defined + in customer's account. This list should always contain one element only. + """ + if len(lambda_function_resources) > 1: + raise InvalidResourceLinkingException("Could not link multiple lambda functions to one Gateway V2 Integration") + + if not lambda_function_resources: + LOG.info( + "Unable to find any references to Lambda functions, skip linking Gateway V2 Integration to Lambda Functions" + ) + return + + logical_id = lambda_function_resources[0] + gateway_v2_integration_cfn_resource["Properties"]["IntegrationUri"] = ( + { + "Fn::Join": [ + "", + [ + "arn:", + {"Ref": "AWS::Partition"}, + ":apigateway:", + {"Ref": "AWS::Region"}, + ":lambda:path/2015-03-31/functions/", + {"Fn::GetAtt": [logical_id.value, "Arn"]}, + "/invocations", + ], + ] + } + if isinstance(logical_id, LogicalIdReference) + else logical_id.value + ) + + +def _link_gateway_v2_integration_to_lambda_function( + v2_gateway_integration_config_resources: Dict[str, TFResource], + v2_gateway_integration_config_address_cfn_resources_map: Dict[str, List], + lambda_functions_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding + Gateway V2 integration resources to each lambda functions + + Parameters + ---------- + v2_gateway_integration_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway Integrations + v2_gateway_integration_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway Integration + lambda_functions_resources: Dict[str, Dict] + Dictionary of all Terraform lambda functions resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=v2_gateway_integration_config_address_cfn_resources_map, + source_resource_tf_config=v2_gateway_integration_config_resources, + destination_resource_tf=lambda_functions_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="invoke_arn", + ), + ], + terraform_link_field_name="integration_uri", + cfn_link_field_name="IntegrationUri", + cfn_resource_update_call_back_function=_link_gateway_v2_integration_to_lambda_function_callback, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_resource_to_api_callback( + gateway_v2_resource_cfn_resource: Dict, gateway_v2_api_resources: List[ReferenceType] +): + """ + Callback function that is used by the linking algorithm to update a CFN V2 Integration Resource with + a reference to the Gateway V2 Api resource + + Parameters + ---------- + gateway_v2_resource_cfn_resource: Dict + API Gateway V2 Integration CFN resource + gateway_v2_api_resources: List[ReferenceType] + List of referenced Gateway V2 Apis either as the logical id of Apis resources + defined in the customer project, or ARN values for actual Api defined + in customer's account. This list should always contain one element only. + """ + if len(gateway_v2_api_resources) > 1: + raise InvalidResourceLinkingException("Could not link multiple Gateway V2 Apis to one Gateway V2 resource") + + if not gateway_v2_api_resources: + LOG.info( + "Unable to find any references to Gateway V2 APIs, skip linking Gateway V2 resources to Gateway V2 APIs" + ) + return + + logical_id = gateway_v2_api_resources[0] + gateway_v2_resource_cfn_resource["Properties"]["ApiId"] = ( + {"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value + ) + + +def _link_gateway_v2_integration_to_api( + gateway_integration_config_resources: Dict[str, TFResource], + gateway_integration_config_address_cfn_resources_map: Dict[str, List], + api_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding + Gateway V2 Integration resources to each Gateway V2 Api + + Parameters + ---------- + gateway_integration_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway Integrations + gateway_integration_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway Integration + api_resources: Dict[str, Dict] + Dictionary of all Terraform Gateway V2 Api resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=gateway_integration_config_address_cfn_resources_map, + source_resource_tf_config=gateway_integration_config_resources, + destination_resource_tf=api_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=_link_gateway_v2_resource_to_api_callback, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_route_to_api( + gateway_route_config_resources: Dict[str, TFResource], + gateway_route_config_address_cfn_resources_map: Dict[str, List], + api_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding + Gateway V2 Route resources to each Gateway V2 Api + + Parameters + ---------- + gateway_route_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway Integrations + gateway_route_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway Route + api_resources: Dict[str, Dict] + Dictionary of all Terraform Gateway V2 Api resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2RouteToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2RouteToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=gateway_route_config_address_cfn_resources_map, + source_resource_tf_config=gateway_route_config_resources, + destination_resource_tf=api_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=_link_gateway_v2_resource_to_api_callback, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_authorizer_to_lambda_function( + authorizer_config_resources: Dict[str, TFResource], + authorizer_cfn_resources: Dict[str, List], + lamda_function_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding V2 Authorizer to each Lambda Function + + Parameters + ---------- + authorizer_config_resources: Dict[str, TFResource] + Dictionary of configuration Authorizer resources + authorizer_cfn_resources: Dict[str, List] + Dictionary containing resolved configuration address of CFN Authorizer resources + lamda_function_resources: Dict[str, Dict] + Dictionary of Terraform Lambda Function resources (not configuration resources). The dictionary's key is the + calculated logical id for each resource + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=authorizer_cfn_resources, + source_resource_tf_config=authorizer_config_resources, + destination_resource_tf=lamda_function_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="invoke_arn", + ), + ], + terraform_link_field_name="authorizer_uri", + cfn_link_field_name="AuthorizerUri", + cfn_resource_update_call_back_function=_link_gateway_authorizer_to_lambda_function_call_back, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_authorizer_to_api( + v2_authorizer_config_resources: Dict[str, TFResource], + v2_authorizer_config_address_cfn_resources_map: Dict[str, List], + api_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding + Gateway V2 Authorizer resources to each Gateway V2 Api + + Parameters + ---------- + v2_authorizer_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway V2 Authorizers + v2_authorizer_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway V2 Authorizer + api_resources: Dict[str, Dict] + Dictionary of all Terraform Gateway V2 Api resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2AuthorizerToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2AuthorizerToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=v2_authorizer_config_address_cfn_resources_map, + source_resource_tf_config=v2_authorizer_config_resources, + destination_resource_tf=api_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=_link_gateway_v2_resource_to_api_callback, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_api_to_function_callback( + gateway_v2_api_cfn_resource: Dict, referenced_function_resource_values: List[ReferenceType] +) -> None: + """ + Callback function that is used by the linking algorithm to update an Api Gateway V2 API CFN Resource with + a reference to the Lambda function resource through the AWS_PROXY integration. + + Parameters + ---------- + gateway_v2_api_cfn_resource: Dict + API Gateway V2 API CFN resource + referenced_function_resource_values: List[ReferenceType] + List of referenced Gateway Resources either as the logical id of Lambda function resource + defined in the customer project, or ARN values for actual Lambda function resource defined + in customer's account. This list should always contain one element only. + """ + if len(referenced_function_resource_values) > 1: + raise InvalidResourceLinkingException("Could not link a V2 API to more than one Lambda Function resources") + + if not referenced_function_resource_values: + LOG.info("Unable to find any references to Lambda functions, skip linking Lambda function to Gateway V2 API") + return + + logical_id = referenced_function_resource_values[0] + gateway_v2_api_cfn_resource["Properties"]["Target"] = ( + {"Fn::Sub": INVOKE_ARN_FORMAT.format(function_logical_id=logical_id.value)} + if isinstance(logical_id, LogicalIdReference) + else logical_id.value + ) + + +def _link_gateway_v2_api_to_function( + gateway_api_config_resources: Dict[str, TFResource], + gateway_api_config_address_cfn_resources_map: Dict[str, List], + lambda_function_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding + Gateway V2 API resources to each Lambda Function + + Parameters + ---------- + gateway_api_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway APIs + gateway_api_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway API + lambda_function_resources: Dict[str, Dict] + Dictionary of all Terraform Lambda Function resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + + # Only link APIs to resources if they are "Quick Create" APIs + quick_create_api_config_resources = { + config_address: tf_resource + for config_address, tf_resource in gateway_api_config_resources.items() + if "target" in tf_resource.attributes + } + + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2ApiToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayV2ApiToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=gateway_api_config_address_cfn_resources_map, + source_resource_tf_config=quick_create_api_config_resources, + destination_resource_tf=lambda_function_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="invoke_arn", + ), + ], + terraform_link_field_name="target", + cfn_link_field_name="Target", + cfn_resource_update_call_back_function=_link_gateway_v2_api_to_function_callback, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_stage_to_api( + gateway_stage_config_resources: Dict[str, TFResource], + gateway_stage_config_address_cfn_resources_map: Dict[str, List], + api_resources: Dict[str, Dict], +): + """ + Iterate through all the resources and link the corresponding + Gateway V2 Stage resources to each Gateway V2 Api + + Parameters + ---------- + gateway_stage_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway Stages + gateway_stage_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway Stage + api_resources: Dict[str, Dict] + Dictionary of all Terraform Gateway V2 Api resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2StageToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2StageToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=gateway_stage_config_address_cfn_resources_map, + source_resource_tf_config=gateway_stage_config_resources, + destination_resource_tf=api_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=_link_gateway_v2_resource_to_api_callback, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_v2_route_to_authorizer_callback( + gateway_v2_route_cfn_resource: Dict, gateway_v2_authorizer_resources: List[ReferenceType] +): + """ + Callback function that is used by the linking algorithm to update a CFN V2 Integration Route with + a reference to the Gateway V2 Authorizer resource + + Parameters + ---------- + gateway_v2_route_cfn_resource: Dict + API Gateway V2 Route CFN resource + gateway_v2_authorizer_resources: List[ReferenceType] + List of referenced Gateway V2 Authorizers either as the logical id of Apis resources + defined in the customer project, or ID values for actual Authorizer defined + in customer's account. This list should always contain one element only. + """ + if len(gateway_v2_authorizer_resources) > 1: + raise InvalidResourceLinkingException("Could not link multiple Gateway V2 Authorizers to one Gateway V2 Route") + + if not gateway_v2_authorizer_resources: + LOG.info( + "Unable to find any references to Gateway V2 Authorizers, skip linking Gateway V2 Routes to Gateway V2 " + "Authorizers" + ) + return + + logical_id = gateway_v2_authorizer_resources[0] + gateway_v2_route_cfn_resource["Properties"]["AuthorizerId"] = ( + {"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value + ) + + +def _link_gateway_v2_route_to_authorizer( + gateway_route_config_resources: Dict[str, TFResource], + gateway_route_config_address_cfn_resources_map: Dict[str, List], + authorizer_resources: Dict[str, Dict], +): + """ + Iterate through all the resources and link the corresponding + Gateway V2 Route resources to each Gateway V2 Authorizer + + Parameters + ---------- + gateway_route_config_resources: Dict[str, TFResource] + Dictionary of configuration Gateway Routes + gateway_route_config_address_cfn_resources_map: Dict[str, List] + Dictionary containing resolved configuration addresses matched up to the cfn Gateway Route + authorizer_resources: Dict[str, Dict] + Dictionary of all Terraform Gateway V2 Authorizer resources (not configuration resources). + The dictionary's key is the calculated logical id for each resource. + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayV2RouteToGatewayV2AuthorizerLinkingLimitationException, + local_variable_linking_exception=GatewayV2RouteToGatewayV2AuthorizerLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=gateway_route_config_address_cfn_resources_map, + source_resource_tf_config=gateway_route_config_resources, + destination_resource_tf=authorizer_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_AUTHORIZER_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="authorizer_id", + cfn_link_field_name="AuthorizerId", + cfn_resource_update_call_back_function=_link_gateway_v2_route_to_authorizer_callback, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() diff --git a/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py b/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py index 356c744e12..cef5e730b6 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py @@ -117,6 +117,51 @@ def __init__(self): super(ApiGatewayAuthorizerProperties, self).__init__() +class ApiGatewayV2RouteProperties(ResourceProperties): + """ + Contains the collection logic of the required properties for linking the aws_api_gateway_v2_route resources. + """ + + def __init__(self): + super(ApiGatewayV2RouteProperties, self).__init__() + + +class ApiGatewayV2ApiProperties(ResourceProperties): + """ + Contains the collection logic of the required properties for linking the aws_apigatewayv2_api resources. + """ + + def __init__(self): + super(ApiGatewayV2ApiProperties, self).__init__() + + +class ApiGatewayV2IntegrationProperties(ResourceProperties): + """ + Contains the collection logic of the required properties for linking the aws_api_gateway_v2_integration resources. + """ + + def __init__(self): + super(ApiGatewayV2IntegrationProperties, self).__init__() + + +class ApiGatewayV2AuthorizerProperties(ResourceProperties): + """ + Contains the collection logic of the required properties for linking the aws_api_gateway_v2_authorizer resources. + """ + + def __init__(self): + super(ApiGatewayV2AuthorizerProperties, self).__init__() + + +class ApiGatewayV2StageProperties(ResourceProperties): + """ + Contains the collection logic of the required properties for linking the aws_api_gateway_v2_stage resources. + """ + + def __init__(self): + super(ApiGatewayV2StageProperties, self).__init__() + + def add_integrations_to_methods( gateway_methods_cfn: Dict[str, List], gateway_integrations_cfn: Dict[str, List] ) -> None: diff --git a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py index 7d9d10f574..128ca34340 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py @@ -8,6 +8,11 @@ TF_AWS_API_GATEWAY_RESOURCE, TF_AWS_API_GATEWAY_REST_API, TF_AWS_API_GATEWAY_STAGE, + TF_AWS_API_GATEWAY_V2_API, + TF_AWS_API_GATEWAY_V2_AUTHORIZER, + TF_AWS_API_GATEWAY_V2_INTEGRATION, + TF_AWS_API_GATEWAY_V2_ROUTE, + TF_AWS_API_GATEWAY_V2_STAGE, TF_AWS_LAMBDA_FUNCTION, TF_AWS_LAMBDA_LAYER_VERSION, ) @@ -25,6 +30,15 @@ _link_gateway_resources_to_gateway_rest_apis, _link_gateway_resources_to_parents, _link_gateway_stage_to_rest_api, + _link_gateway_v2_api_to_function, + _link_gateway_v2_authorizer_to_api, + _link_gateway_v2_authorizer_to_lambda_function, + _link_gateway_v2_integration_to_api, + _link_gateway_v2_integration_to_lambda_function, + _link_gateway_v2_route_to_api, + _link_gateway_v2_route_to_authorizer, + _link_gateway_v2_route_to_integration, + _link_gateway_v2_stage_to_api, _link_lambda_functions_to_layers, ) from samcli.hook_packages.terraform.hooks.prepare.types import ( @@ -79,6 +93,51 @@ dest=TF_AWS_API_GATEWAY_AUTHORIZER, linking_func=_link_gateway_method_to_gateway_authorizer, ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_ROUTE, + dest=TF_AWS_API_GATEWAY_V2_INTEGRATION, + linking_func=_link_gateway_v2_route_to_integration, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_INTEGRATION, + dest=TF_AWS_LAMBDA_FUNCTION, + linking_func=_link_gateway_v2_integration_to_lambda_function, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_INTEGRATION, + dest=TF_AWS_API_GATEWAY_V2_API, + linking_func=_link_gateway_v2_integration_to_api, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_ROUTE, + dest=TF_AWS_API_GATEWAY_V2_API, + linking_func=_link_gateway_v2_route_to_api, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_AUTHORIZER, + dest=TF_AWS_LAMBDA_FUNCTION, + linking_func=_link_gateway_v2_authorizer_to_lambda_function, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_AUTHORIZER, + dest=TF_AWS_API_GATEWAY_V2_API, + linking_func=_link_gateway_v2_authorizer_to_api, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_API, + dest=TF_AWS_LAMBDA_FUNCTION, + linking_func=_link_gateway_v2_api_to_function, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_STAGE, + dest=TF_AWS_API_GATEWAY_V2_API, + linking_func=_link_gateway_v2_stage_to_api, + ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_V2_ROUTE, + dest=TF_AWS_API_GATEWAY_V2_AUTHORIZER, + linking_func=_link_gateway_v2_route_to_authorizer, + ), ] MULTIPLE_DESTINATIONS_RESOURCE_LINKS: List[LinkingMultipleDestinationsOptionsCaller] = [ diff --git a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py index f190eb43d2..76931dd22b 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py @@ -9,6 +9,11 @@ TF_AWS_API_GATEWAY_RESOURCE, TF_AWS_API_GATEWAY_REST_API, TF_AWS_API_GATEWAY_STAGE, + TF_AWS_API_GATEWAY_V2_API, + TF_AWS_API_GATEWAY_V2_AUTHORIZER, + TF_AWS_API_GATEWAY_V2_INTEGRATION, + TF_AWS_API_GATEWAY_V2_ROUTE, + TF_AWS_API_GATEWAY_V2_STAGE, TF_AWS_LAMBDA_FUNCTION, TF_AWS_LAMBDA_LAYER_VERSION, ) @@ -18,6 +23,11 @@ ApiGatewayResourceProperties, ApiGatewayRestApiProperties, ApiGatewayStageProperties, + ApiGatewayV2ApiProperties, + ApiGatewayV2AuthorizerProperties, + ApiGatewayV2IntegrationProperties, + ApiGatewayV2RouteProperties, + ApiGatewayV2StageProperties, ) from samcli.hook_packages.terraform.hooks.prepare.resources.internal import ( InternalApiGatewayIntegrationProperties, @@ -49,4 +59,9 @@ def get_resource_property_mapping() -> Dict[str, ResourceProperties]: TF_AWS_API_GATEWAY_INTEGRATION: InternalApiGatewayIntegrationProperties(), TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE: InternalApiGatewayIntegrationResponseProperties(), TF_AWS_API_GATEWAY_AUTHORIZER: ApiGatewayAuthorizerProperties(), + TF_AWS_API_GATEWAY_V2_ROUTE: ApiGatewayV2RouteProperties(), + TF_AWS_API_GATEWAY_V2_INTEGRATION: ApiGatewayV2IntegrationProperties(), + TF_AWS_API_GATEWAY_V2_API: ApiGatewayV2ApiProperties(), + TF_AWS_API_GATEWAY_V2_AUTHORIZER: ApiGatewayV2AuthorizerProperties(), + TF_AWS_API_GATEWAY_V2_STAGE: ApiGatewayV2StageProperties(), } diff --git a/samcli/hook_packages/terraform/hooks/prepare/translate.py b/samcli/hook_packages/terraform/hooks/prepare/translate.py index 2d8639de4f..bd32102eb2 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/translate.py +++ b/samcli/hook_packages/terraform/hooks/prepare/translate.py @@ -15,6 +15,7 @@ TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE, TF_AWS_API_GATEWAY_METHOD, TF_AWS_API_GATEWAY_REST_API, + TF_AWS_API_GATEWAY_V2_API, ) from samcli.hook_packages.terraform.hooks.prepare.enrich import enrich_resources_and_generate_makefile from samcli.hook_packages.terraform.hooks.prepare.property_builder import ( @@ -68,6 +69,7 @@ TRANSLATION_VALIDATORS: Dict[str, Type[ResourceTranslationValidator]] = { TF_AWS_API_GATEWAY_REST_API: RESTAPITranslationValidator, + TF_AWS_API_GATEWAY_V2_API: RESTAPITranslationValidator, } diff --git a/tests/integration/buildcmd/test_build_terraform_applications.py b/tests/integration/buildcmd/test_build_terraform_applications.py index 8a0044d4e7..983571b184 100644 --- a/tests/integration/buildcmd/test_build_terraform_applications.py +++ b/tests/integration/buildcmd/test_build_terraform_applications.py @@ -15,7 +15,6 @@ from parameterized import parameterized, parameterized_class -from samcli.lib.utils.colors import Colored from tests.integration.buildcmd.build_integ_base import BuildIntegBase from tests.testing_utils import CI_OVERRIDE, IS_WINDOWS, RUN_BY_CANARY @@ -76,7 +75,6 @@ def _verify_invoke_built_function(self, function_logical_id, overrides, expected "--no-event", "--hook-name", "terraform", - "--beta-features", ] if overrides: @@ -243,7 +241,6 @@ def setUpClass(cls): @parameterized.expand(functions) def test_build_and_invoke_lambda_functions(self, function_identifier, expected_output, should_override_code): command_list_parameters = { - "beta_features": True, "hook_name": "terraform", "function_identifier": function_identifier, } @@ -260,18 +257,6 @@ def test_build_and_invoke_lambda_functions(self, function_identifier, expected_o if should_override_code: environment_variables["TF_VAR_HELLO_FUNCTION_SRC_CODE"] = "./artifacts/HelloWorldFunction2" stdout, stderr, return_code = self.run_command(build_cmd_list, env=environment_variables) - terraform_beta_feature_prompted_text = ( - f"Supporting Terraform applications is a beta feature.{os.linesep}" - f"Please confirm if you would like to proceed using AWS SAM CLI with terraform application.{os.linesep}" - "You can also enable this beta feature with 'sam build --beta-features'." - ) - experimental_warning = ( - f"{os.linesep}Experimental features are enabled for this session.{os.linesep}" - f"Visit the docs page to learn more about the AWS Beta terms " - f"https://aws.amazon.com/service-terms/.{os.linesep}" - ) - self.assertNotRegex(stdout.decode("utf-8"), terraform_beta_feature_prompted_text) - self.assertIn(Colored().yellow(experimental_warning), stderr.decode("utf-8")) LOG.info("sam build stdout: %s", stdout.decode("utf-8")) LOG.info("sam build stderr: %s", stderr.decode("utf-8")) self.assertEqual(return_code, 0) @@ -374,7 +359,6 @@ def setUpClass(cls): @parameterized.expand(functions) def test_build_and_invoke_lambda_functions(self, function_identifier, expected_output, should_override_code): command_list_parameters = { - "beta_features": True, "hook_name": "terraform", "function_identifier": function_identifier, } diff --git a/tests/integration/buildcmd/test_build_terraform_applications_other_cases.py b/tests/integration/buildcmd/test_build_terraform_applications_other_cases.py index ea57300a2f..c1efc9f7ef 100644 --- a/tests/integration/buildcmd/test_build_terraform_applications_other_cases.py +++ b/tests/integration/buildcmd/test_build_terraform_applications_other_cases.py @@ -6,7 +6,6 @@ from parameterized import parameterized, parameterized_class -from samcli.lib.utils.colors import Colored from tests.integration.buildcmd.test_build_terraform_applications import ( BuildTerraformApplicationIntegBase, BuildTerraformApplicationS3BackendIntegBase, @@ -44,7 +43,7 @@ def test_invalid_hook_name(self): self.assertNotEqual(return_code, 0) def test_exit_failed_use_container_no_build_image_hooks(self): - cmdlist = self.get_command_list(beta_features=True, hook_name="terraform", use_container=True) + cmdlist = self.get_command_list(hook_name="terraform", use_container=True) _, stderr, return_code = self.run_command(cmdlist) process_stderr = stderr.strip() self.assertRegex( @@ -54,7 +53,7 @@ def test_exit_failed_use_container_no_build_image_hooks(self): self.assertNotEqual(return_code, 0) def test_exit_failed_project_root_dir_no_hooks(self): - cmdlist = self.get_command_list(beta_features=True, project_root_dir="/path") + cmdlist = self.get_command_list(project_root_dir="/path") _, stderr, return_code = self.run_command(cmdlist) process_stderr = stderr.strip() self.assertRegex( @@ -64,7 +63,7 @@ def test_exit_failed_project_root_dir_no_hooks(self): self.assertNotEqual(return_code, 0) def test_exit_failed_project_root_dir_not_parent_of_current_directory(self): - cmdlist = self.get_command_list(beta_features=True, hook_name="terraform", project_root_dir="/path") + cmdlist = self.get_command_list(hook_name="terraform", project_root_dir="/path") _, stderr, return_code = self.run_command(cmdlist) process_stderr = stderr.strip() self.assertRegex( @@ -75,7 +74,7 @@ def test_exit_failed_project_root_dir_not_parent_of_current_directory(self): self.assertNotEqual(return_code, 0) def test_exit_failed_use_container_short_format_no_build_image_hooks(self): - cmdlist = self.get_command_list(beta_features=True, hook_name="terraform") + cmdlist = self.get_command_list(hook_name="terraform") cmdlist += ["-u"] _, stderr, return_code = self.run_command(cmdlist) process_stderr = stderr.strip() @@ -85,53 +84,6 @@ def test_exit_failed_use_container_short_format_no_build_image_hooks(self): ) self.assertNotEqual(return_code, 0) - def test_exit_success_no_beta_feature_flags_hooks(self): - cmdlist = self.get_command_list(beta_features=None, hook_name="terraform") - stdout, stderr, return_code = self.run_command(cmdlist, input=b"N\n\n") - terraform_beta_feature_prompted_text = ( - f"Supporting Terraform applications is a beta feature.{os.linesep}" - f"Please confirm if you would like to proceed using AWS SAM CLI with terraform application.{os.linesep}" - "You can also enable this beta feature with 'sam build --beta-features'." - ) - self.assertRegex(stdout.decode("utf-8"), terraform_beta_feature_prompted_text) - self.assertEqual(return_code, 0) - self.assertRegex(stderr.strip().decode("utf-8"), "Terraform Support beta feature is not enabled.") - - def test_exit_success_no_beta_features_flags_supplied_hooks(self): - cmdlist = self.get_command_list(beta_features=False, hook_name="terraform") - _, stderr, return_code = self.run_command(cmdlist) - self.assertEqual(return_code, 0) - self.assertRegex(stderr.strip().decode("utf-8"), "Terraform Support beta feature is not enabled.") - - def test_build_terraform_with_no_beta_feature_option_in_samconfig_toml(self): - samconfig_toml_path = Path(self.working_dir).joinpath("samconfig.toml") - samconfig_lines = [ - bytes("version = 0.1" + os.linesep, "utf-8"), - bytes("[default.global.parameters]" + os.linesep, "utf-8"), - bytes("beta_features = false" + os.linesep, "utf-8"), - ] - with open(samconfig_toml_path, "wb") as file: - file.writelines(samconfig_lines) - - cmdlist = self.get_command_list(hook_name="terraform") - _, stderr, return_code = self.run_command(cmdlist) - self.assertEqual(return_code, 0) - self.assertRegex(stderr.strip().decode("utf-8"), "Terraform Support beta feature is not enabled.") - # delete the samconfig file - try: - os.remove(samconfig_toml_path) - except FileNotFoundError: - pass - - def test_build_terraform_with_no_beta_feature_option_as_environment_variable(self): - environment_variables = os.environ.copy() - environment_variables["SAM_CLI_BETA_TERRAFORM_SUPPORT"] = "False" - - build_command_list = self.get_command_list(hook_name="terraform") - _, stderr, return_code = self.run_command(build_command_list, env=environment_variables) - self.assertEqual(return_code, 0) - self.assertRegex(stderr.strip().decode("utf-8"), "Terraform Support beta feature is not enabled.") - @skipIf( (not RUN_BY_CANARY and not CI_OVERRIDE), @@ -142,9 +94,7 @@ class TestInvalidTerraformApplicationThatReferToS3BucketNotCreatedYet(BuildTerra def test_invoke_function(self): function_identifier = "aws_lambda_function.function" - build_cmd_list = self.get_command_list( - beta_features=True, hook_name="terraform", function_identifier=function_identifier - ) + build_cmd_list = self.get_command_list(hook_name="terraform", function_identifier=function_identifier) LOG.info("command list: %s", build_cmd_list) environment_variables = os.environ.copy() @@ -175,7 +125,6 @@ class TestInvalidBuildTerraformApplicationsWithZipBasedLambdaFunctionAndS3Backen def test_build_no_s3_config(self): command_list_parameters = { - "beta_features": True, "hook_name": "terraform", } build_cmd_list = self.get_command_list(**command_list_parameters) @@ -206,9 +155,7 @@ class TestBuildTerraformApplicationsWithImageBasedLambdaFunctionAndLocalBackend( @parameterized.expand(functions) def test_build_and_invoke_lambda_functions(self, function_identifier): - build_cmd_list = self.get_command_list( - beta_features=True, hook_name="terraform", function_identifier=function_identifier - ) + build_cmd_list = self.get_command_list(hook_name="terraform", function_identifier=function_identifier) LOG.info("command list: %s", build_cmd_list) _, stderr, return_code = self.run_command(build_cmd_list) LOG.info(stderr) @@ -248,9 +195,7 @@ class TestBuildTerraformApplicationsWithImageBasedLambdaFunctionAndS3Backend( @parameterized.expand(functions) def test_build_and_invoke_lambda_functions(self, function_identifier): - build_cmd_list = self.get_command_list( - beta_features=True, hook_name="terraform", function_identifier=function_identifier - ) + build_cmd_list = self.get_command_list(hook_name="terraform", function_identifier=function_identifier) LOG.info("command list: %s", build_cmd_list) _, stderr, return_code = self.run_command(build_cmd_list) LOG.info(stderr) @@ -312,7 +257,7 @@ def test_unsupported_cases(self, app, expected_error_message): self.terraform_application_path = Path(self.terraform_application_path) / app shutil.copytree(Path(self.terraform_application_path), Path(self.working_dir)) - build_cmd_list = self.get_command_list(beta_features=True, hook_name="terraform") + build_cmd_list = self.get_command_list(hook_name="terraform") LOG.info("command list: %s", build_cmd_list) _, stderr, return_code = self.run_command(build_cmd_list) LOG.info(stderr) @@ -370,7 +315,7 @@ def tearDown(self): self.assertEqual(return_code, 0) def test_unsupported_cases_runs_after_apply(self): - build_cmd_list = self.get_command_list(beta_features=True, hook_name="terraform") + build_cmd_list = self.get_command_list(hook_name="terraform") LOG.info("command list: %s", build_cmd_list) _, _, return_code = self.run_command(build_cmd_list) self.assertEqual(return_code, 0) @@ -390,9 +335,7 @@ class TestBuildGoFunctionAndKeepPermissions(BuildTerraformApplicationIntegBase): def test_invoke_function(self): function_identifier = "hello-world-function" - build_cmd_list = self.get_command_list( - beta_features=True, hook_name="terraform", function_identifier=function_identifier - ) + build_cmd_list = self.get_command_list(hook_name="terraform", function_identifier=function_identifier) LOG.info("command list: %s", build_cmd_list) environment_variables = os.environ.copy() @@ -461,7 +404,6 @@ def tearDown(self): @parameterized.expand(functions) def test_build_and_invoke_lambda_functions(self, function_identifier, expected_output): command_list_parameters = { - "beta_features": True, "hook_name": "terraform", "function_identifier": function_identifier, "project_root_dir": "./..", @@ -472,18 +414,6 @@ def test_build_and_invoke_lambda_functions(self, function_identifier, expected_o build_cmd_list = self.get_command_list(**command_list_parameters) LOG.info("command list: %s", build_cmd_list) stdout, stderr, return_code = self.run_command(build_cmd_list) - terraform_beta_feature_prompted_text = ( - f"Supporting Terraform applications is a beta feature.{os.linesep}" - f"Please confirm if you would like to proceed using AWS SAM CLI with terraform application.{os.linesep}" - "You can also enable this beta feature with 'sam build --beta-features'." - ) - experimental_warning = ( - f"{os.linesep}Experimental features are enabled for this session.{os.linesep}" - f"Visit the docs page to learn more about the AWS Beta terms " - f"https://aws.amazon.com/service-terms/.{os.linesep}" - ) - self.assertNotRegex(stdout.decode("utf-8"), terraform_beta_feature_prompted_text) - self.assertIn(Colored().yellow(experimental_warning), stderr.decode("utf-8")) LOG.info("sam build stdout: %s", stdout.decode("utf-8")) LOG.info("sam build stderr: %s", stderr.decode("utf-8")) self.assertEqual(return_code, 0) @@ -527,18 +457,6 @@ def test_build_and_invoke_lambda_functions(self, function_identifier, expected_o build_cmd_list = self.get_command_list(**command_list_parameters) LOG.info("command list: %s", build_cmd_list) stdout, stderr, return_code = self.run_command(build_cmd_list) - terraform_beta_feature_prompted_text = ( - f"Supporting Terraform applications is a beta feature.{os.linesep}" - f"Please confirm if you would like to proceed using AWS SAM CLI with terraform application.{os.linesep}" - "You can also enable this beta feature with 'sam build --beta-features'." - ) - experimental_warning = ( - f"{os.linesep}Experimental features are enabled for this session.{os.linesep}" - f"Visit the docs page to learn more about the AWS Beta terms " - f"https://aws.amazon.com/service-terms/.{os.linesep}" - ) - self.assertNotRegex(stdout.decode("utf-8"), terraform_beta_feature_prompted_text) - self.assertIn(Colored().yellow(experimental_warning), stderr.decode("utf-8")) LOG.info("sam build stdout: %s", stdout.decode("utf-8")) LOG.info("sam build stderr: %s", stderr.decode("utf-8")) self.assertEqual(return_code, 0) diff --git a/tests/integration/local/common_utils.py b/tests/integration/local/common_utils.py index 9ed98ed361..0dbb23aa7c 100644 --- a/tests/integration/local/common_utils.py +++ b/tests/integration/local/common_utils.py @@ -28,11 +28,7 @@ def wait_for_local_process(process, port, collect_output=False) -> str: if "Address already in use" in line_as_str: LOG.info(f"Attempted to start port on {port} but it is already in use, restarting on a new port.") raise InvalidAddressException() - if ( - "Press CTRL+C to quit" in line_as_str - or "Terraform Support beta feature is not enabled." in line_as_str - or "Error: " in line_as_str - ): + if "Press CTRL+C to quit" in line_as_str or "Error: " in line_as_str: break return output diff --git a/tests/integration/local/invoke/test_invoke_terraform_applications.py b/tests/integration/local/invoke/test_invoke_terraform_applications.py index 792025f65d..e810696118 100644 --- a/tests/integration/local/invoke/test_invoke_terraform_applications.py +++ b/tests/integration/local/invoke/test_invoke_terraform_applications.py @@ -15,11 +15,8 @@ from docker.errors import APIError from parameterized import parameterized, parameterized_class -from samcli.commands._utils.experimental import EXPERIMENTAL_WARNING -from samcli.lib.utils.colors import Colored from tests.integration.local.invoke.invoke_integ_base import InvokeIntegBase, TIMEOUT from tests.integration.local.invoke.layer_utils import LayerUtils -from tests.integration.local.start_lambda.start_lambda_api_integ_base import StartLambdaIntegBaseClass from tests.testing_utils import CI_OVERRIDE, IS_WINDOWS, RUNNING_ON_CI, RUN_BY_CANARY LOG = logging.getLogger(__name__) @@ -76,7 +73,7 @@ def tearDown(self) -> None: @pytest.mark.flaky(reruns=3) def test_invoke_function(self, function_name): local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke=function_name, hook_name="terraform", beta_features=True + function_to_invoke=function_name, hook_name="terraform" ) stdout, _, return_code = self.run_command(local_invoke_command_list) @@ -97,7 +94,6 @@ def test_invoke_function_custom_plan(self, function_name): local_invoke_command_list = InvokeIntegBase.get_command_list( function_to_invoke=function_name, hook_name="terraform", - beta_features=True, terraform_plan_file="custom-plan.json", ) stdout, _, return_code = self.run_command(local_invoke_command_list) @@ -110,7 +106,7 @@ def test_invoke_function_custom_plan(self, function_name): self.assertEqual(response, expected_response) def test_exit_failed_project_root_dir_no_hooks_custom_plan_file(self): - cmdlist = self.get_command_list(beta_features=True, terraform_plan_file="/path", function_to_invoke="") + cmdlist = self.get_command_list(terraform_plan_file="/path", function_to_invoke="") _, stderr, return_code = self.run_command(cmdlist) process_stderr = stderr.strip() self.assertRegex( @@ -119,137 +115,6 @@ def test_exit_failed_project_root_dir_no_hooks_custom_plan_file(self): ) self.assertNotEqual(return_code, 0) - @skipIf( - not CI_OVERRIDE, - "Skip Terraform test cases unless running in CI", - ) - @parameterized.expand(functions) - @pytest.mark.flaky(reruns=3) - def test_invoke_terraform_with_beta_feature_option_in_samconfig_toml(self, function_name): - samconfig_toml_path = Path(self.terraform_application_path).joinpath("samconfig.toml") - samconfig_lines = [ - bytes("version = 0.1" + os.linesep, "utf-8"), - bytes("[default.global.parameters]" + os.linesep, "utf-8"), - bytes("beta_features = true" + os.linesep, "utf-8"), - ] - with open(samconfig_toml_path, "wb") as file: - file.writelines(samconfig_lines) - - local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke=function_name, hook_name="terraform" - ) - stdout, _, return_code = self.run_command(local_invoke_command_list) - - # Get the response without the sam-cli prompts that proceed it - response = json.loads(stdout.decode("utf-8").split("\n")[-1]) - expected_response = json.loads('{"statusCode":200,"body":"{\\"message\\": \\"hello world\\"}"}') - - self.assertEqual(return_code, 0) - self.assertEqual(response, expected_response) - # delete the samconfig file - try: - os.remove(samconfig_toml_path) - except FileNotFoundError: - pass - - @skipIf( - not CI_OVERRIDE, - "Skip Terraform test cases unless running in CI", - ) - @parameterized.expand(functions) - @pytest.mark.flaky(reruns=3) - def test_invoke_terraform_with_beta_feature_option_as_environment_variable(self, function_name): - environment_variables = os.environ.copy() - environment_variables["SAM_CLI_BETA_TERRAFORM_SUPPORT"] = "1" - - local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke=function_name, hook_name="terraform" - ) - stdout, _, return_code = self.run_command(local_invoke_command_list, env=environment_variables) - - # Get the response without the sam-cli prompts that proceed it - response = json.loads(stdout.decode("utf-8").split("\n")[-1]) - expected_response = json.loads('{"statusCode":200,"body":"{\\"message\\": \\"hello world\\"}"}') - - self.assertEqual(return_code, 0) - self.assertEqual(response, expected_response) - - @skipIf( - not CI_OVERRIDE, - "Skip Terraform test cases unless running in CI", - ) - @pytest.mark.flaky(reruns=3) - def test_invoke_function_get_experimental_prompted(self): - local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke="s3_lambda_function", hook_name="terraform" - ) - stdout, stderr, return_code = self.run_command(local_invoke_command_list, input=b"Y\n\n") - - terraform_beta_feature_prompted_text = ( - "Supporting Terraform applications is a beta feature.\n" - "Please confirm if you would like to proceed using AWS SAM CLI with terraform application.\n" - "You can also enable this beta feature with 'sam local invoke --beta-features'." - ) - self.assertRegex(stdout.decode("utf-8"), terraform_beta_feature_prompted_text) - self.assertRegex(stderr.decode("utf-8"), EXPERIMENTAL_WARNING) - - response = json.loads(stdout.decode("utf-8").split("\n")[2][85:].strip()) - expected_response = json.loads('{"statusCode":200,"body":"{\\"message\\": \\"hello world\\"}"}') - - self.assertEqual(return_code, 0) - self.assertEqual(response, expected_response) - - @skipIf( - not CI_OVERRIDE, - "Skip Terraform test cases unless running in CI", - ) - @pytest.mark.flaky(reruns=3) - def test_invoke_function_with_beta_feature_expect_warning_message(self): - local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke="s3_lambda_function", hook_name="terraform", beta_features=True - ) - stdout, stderr, return_code = self.run_command(local_invoke_command_list) - - terraform_beta_feature_prompted_text = ( - "Supporting Terraform applications is a beta feature.\n" - "Please confirm if you would like to proceed using AWS SAM CLI with terraform application.\n" - "You can also enable this beta feature with 'sam local invoke --beta-features'." - ) - self.assertNotRegex(stdout.decode("utf-8"), terraform_beta_feature_prompted_text) - self.assertTrue(Colored().yellow(EXPERIMENTAL_WARNING) in stderr.decode("utf-8")) - - response = json.loads(stdout.decode("utf-8").split("\n")[-1]) - expected_response = json.loads('{"statusCode":200,"body":"{\\"message\\": \\"hello world\\"}"}') - - self.assertEqual(return_code, 0) - self.assertEqual(response, expected_response) - - @skipIf( - not CI_OVERRIDE, - "Skip Terraform test cases unless running in CI", - ) - @pytest.mark.flaky(reruns=3) - def test_invoke_function_get_experimental_prompted_input_no(self): - local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke="s3_lambda_function", hook_name="terraform" - ) - stdout, stderr, return_code = self.run_command(local_invoke_command_list, input=b"N\n\n") - - terraform_beta_feature_prompted_text = ( - "Supporting Terraform applications is a beta feature.\n" - "Please confirm if you would like to proceed using AWS SAM CLI with terraform application.\n" - "You can also enable this beta feature with 'sam local invoke --beta-features'." - ) - self.assertRegex(stdout.decode("utf-8"), terraform_beta_feature_prompted_text) - - process_stderr = stderr.strip() - self.assertRegex( - process_stderr.decode("utf-8"), - "Terraform Support beta feature is not enabled.", - ) - - self.assertEqual(return_code, 0) - def test_invalid_hook_name(self): local_invoke_command_list = InvokeIntegBase.get_command_list("func", hook_name="tf") _, stderr, return_code = self.run_command(local_invoke_command_list) @@ -261,58 +126,6 @@ def test_invalid_hook_name(self): ) self.assertNotEqual(return_code, 0) - def test_invoke_terraform_with_no_beta_feature_option(self): - local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke="func", hook_name="terraform", beta_features=False - ) - _, stderr, return_code = self.run_command(local_invoke_command_list) - - process_stderr = stderr.strip() - self.assertRegex( - process_stderr.decode("utf-8"), - "Terraform Support beta feature is not enabled.", - ) - self.assertEqual(return_code, 0) - - def test_invoke_terraform_with_no_beta_feature_option_in_samconfig_toml(self): - samconfig_toml_path = Path(self.terraform_application_path).joinpath("samconfig.toml") - samconfig_lines = [ - bytes("version = 0.1" + os.linesep, "utf-8"), - bytes("[default.global.parameters]" + os.linesep, "utf-8"), - bytes("beta_features = false" + os.linesep, "utf-8"), - ] - with open(samconfig_toml_path, "wb") as file: - file.writelines(samconfig_lines) - - local_invoke_command_list = InvokeIntegBase.get_command_list(function_to_invoke="func", hook_name="terraform") - _, stderr, return_code = self.run_command(local_invoke_command_list) - - process_stderr = stderr.strip() - self.assertRegex( - process_stderr.decode("utf-8"), - "Terraform Support beta feature is not enabled.", - ) - self.assertEqual(return_code, 0) - # delete the samconfig file - try: - os.remove(samconfig_toml_path) - except FileNotFoundError: - pass - - def test_invoke_terraform_with_no_beta_feature_option_as_environment_variable(self): - environment_variables = os.environ.copy() - environment_variables["SAM_CLI_BETA_TERRAFORM_SUPPORT"] = "False" - - local_invoke_command_list = InvokeIntegBase.get_command_list(function_to_invoke="func", hook_name="terraform") - _, stderr, return_code = self.run_command(local_invoke_command_list, env=environment_variables) - - process_stderr = stderr.strip() - self.assertRegex( - process_stderr.decode("utf-8"), - "Terraform Support beta feature is not enabled.", - ) - self.assertEqual(return_code, 0) - def test_invalid_coexist_parameters(self): local_invoke_command_list = InvokeIntegBase.get_command_list( "func", hook_name="terraform", template_path="template.yaml" @@ -474,7 +287,7 @@ def tearDown(self) -> None: @pytest.mark.flaky(reruns=3) def test_invoke_function(self, function_name, expected_output): local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke=function_name, hook_name="terraform", beta_features=True + function_to_invoke=function_name, hook_name="terraform" ) stdout, _, return_code = self.run_command(local_invoke_command_list, env=self._add_tf_project_variables()) @@ -506,7 +319,7 @@ def tearDown(self) -> None: def test_invoke_function(self): function_name = "aws_lambda_function.function" local_invoke_command_list = InvokeIntegBase.get_command_list( - function_to_invoke=function_name, hook_name="terraform", beta_features=True + function_to_invoke=function_name, hook_name="terraform" ) _, stderr, return_code = self.run_command(local_invoke_command_list) process_stderr = stderr.strip() @@ -566,7 +379,6 @@ def test_invoke_image_function(self, function_name): function_to_invoke=function_name, hook_name="terraform", event_path=self.event_path, - beta_features=True, ) stdout, _, return_code = self.run_command(local_invoke_command_list) diff --git a/tests/integration/local/start_api/test_start_api_with_terraform_application.py b/tests/integration/local/start_api/test_start_api_with_terraform_application.py index 68725170f1..74ce8743a1 100644 --- a/tests/integration/local/start_api/test_start_api_with_terraform_application.py +++ b/tests/integration/local/start_api/test_start_api_with_terraform_application.py @@ -27,7 +27,7 @@ def setUpClass(cls): command = get_sam_command() cls.template_path = "" cls.build_before_invoke = False - cls.command_list = [command, "local", "start-api", "--hook-name", "terraform", "--beta-features"] + cls.command_list = [command, "local", "start-api", "--hook-name", "terraform"] if cls.terraform_plan_file: cls.command_list += ["--terraform-plan-file", cls.terraform_plan_file] cls.test_data_path = Path(cls.get_integ_dir()) / "testdata" / "start_api" @@ -97,6 +97,7 @@ def tearDownClass(cls) -> None: not CI_OVERRIDE, "Skip Terraform test cases unless running in CI", ) +@pytest.mark.flaky(reruns=3) @parameterized_class( [ { @@ -107,9 +108,16 @@ def tearDownClass(cls) -> None: "terraform_application": "terraform-v1-api-simple", "testing_urls": ["hello"], }, + { + "terraform_application": "terraform-v2-api-simple", + "testing_urls": ["hello"], + }, + { + "terraform_application": "terraform-v2-api-quick-create", + "testing_urls": ["hello"], + }, ] ) -@pytest.mark.flaky(reruns=3) class TestStartApiTerraformApplication(TerraformStartApiIntegrationBase): def setUp(self): self.url = "http://127.0.0.1:{}".format(self.port) @@ -164,6 +172,21 @@ def test_successful_request(self): "expected_error_message": "Error: AWS SAM CLI could not process a Terraform project that uses local " "variables to define linked resources.", }, + { + "terraform_application": "terraform-v2-api-simple-multi-resource-link", + "expected_error_message": "Error: AWS SAM CLI could not process a Terraform project that contains a source " + "resource that is linked to more than one destination resource.", + }, + { + "terraform_application": "terraform-v2-api-simple-local-resource-link", + "expected_error_message": "Error: AWS SAM CLI could not process a Terraform project that uses local " + "variables to define linked resources.", + }, + { + "terraform_application": "terraform-v2-openapi", + "expected_error_message": "Error: AWS SAM CLI is unable to process a Terraform project that uses an OpenAPI" + " specification to define the API Gateway resource.", + }, ] ) class TestStartApiTerraformApplicationLimitations(TerraformStartApiIntegrationBase): @@ -176,7 +199,6 @@ def setUpClass(cls): "start-api", "--hook-name", "terraform", - "--beta-features", "-p", str(random_port()), ] @@ -222,6 +244,26 @@ def test_unsupported_limitations(self): "terraform_application": "terraform-api-simple-local-variables-limitation", "testing_urls": ["hello"], }, + { + "terraform_application": "terraform-v2-api-simple-multi-resource-link", + "testing_urls": ["hello"], + }, + { + "terraform_application": "terraform-v2-api-simple-local-resource-link", + "testing_urls": ["hello"], + }, + { + "terraform_application": "terraform-v2-openapi", + "testing_urls": ["hello"], + }, + { + "terraform_application": "terraform-v2-api-simple", + "testing_urls": ["hello"], + }, + { + "terraform_application": "terraform-v2-api-quick-create", + "testing_urls": ["hello"], + }, ] ) class TestStartApiTerraformApplicationLimitationsAfterApply(TerraformStartApiIntegrationApplyBase): @@ -240,22 +282,32 @@ def test_successful_request(self): not CI_OVERRIDE, "Skip Terraform test cases unless running in CI", ) +@parameterized_class( + [ + { + "terraform_application": "v1-lambda-authorizer", + "gateway_version": "v1", + }, + { + "terraform_application": "v2-lambda-authorizer", + "gateway_version": "v2", + }, + ] +) @pytest.mark.flaky(reruns=3) -class TestStartApiTerraformApplicationV1LambdaAuthorizers(TerraformStartApiIntegrationBase): - terraform_application = "v1-lambda-authorizer" - +class TestStartApiTerraformApplicationLambdaAuthorizers(TerraformStartApiIntegrationBase): def setUp(self): self.url = "http://127.0.0.1:{}".format(self.port) @parameterized.expand( [ - ("/hello", {"headers": {"myheader": "123"}}), - ("/hello-request", {"headers": {"myheader": "123"}, "params": {"mystring": "456"}}), - ("/hello-request-empty", {}), - ("/hello-request-empty", {"headers": {"foo": "bar"}}), + ("/hello", {"headers": {"myheader": "123"}}, ["v1", "v2"]), + ("/hello-request", {"headers": {"myheader": "123"}, "params": {"mystring": "456"}}, ["v1", "v2"]), ] ) - def test_invoke_authorizer(self, endpoint, parameters): + def test_invoke_authorizer(self, endpoint, parameters, applicable_gateway_versions): + if self.gateway_version not in applicable_gateway_versions: + self.skipTest(f"This test case is not supported for {self.gateway_version} api gateway") response = requests.get(self.url + endpoint, timeout=300, **parameters) self.assertEqual(response.status_code, 200) @@ -263,16 +315,20 @@ def test_invoke_authorizer(self, endpoint, parameters): @parameterized.expand( [ - ("/hello", {"headers": {"blank": "invalid"}}), - ("/hello-request", {"headers": {"blank": "invalid"}, "params": {"blank": "invalid"}}), + ("/hello", {"headers": {"blank": "invalid"}}, ["v1", "v2"]), + ("/hello-request", {"headers": {"blank": "invalid"}, "params": {"blank": "invalid"}}, ["v1", "v2"]), ] ) - def test_missing_authorizer_identity_source(self, endpoint, parameters): + def test_missing_authorizer_identity_source(self, endpoint, parameters, applicable_gateway_versions): + if self.gateway_version not in applicable_gateway_versions: + self.skipTest(f"This test case is not supported for {self.gateway_version} api gateway") response = requests.get(self.url + endpoint, timeout=300, **parameters) self.assertEqual(response.status_code, 401) def test_fails_token_header_validation_authorizer(self): + if self.gateway_version not in ["v1"]: + self.skipTest(f"This test case is not supported for {self.gateway_version} api gateway") response = requests.get(self.url + "/hello", timeout=300, headers={"myheader": "not valid"}) self.assertEqual(response.status_code, 401) @@ -282,20 +338,40 @@ def test_fails_token_header_validation_authorizer(self): not CI_OVERRIDE, "Skip Terraform test cases unless running in CI", ) +@parameterized_class( + [ + { + "terraform_application": "v1-lambda-authorizer", + "gateway_version": "v1", + }, + { + "terraform_application": "lambda-auth-openapi", + "gateway_version": "v1", + }, + { + "terraform_application": "terraform-v2-auth-openapi", + "gateway_version": "v2", + }, + { + "terraform_application": "v2-lambda-authorizer", + "gateway_version": "v2", + }, + ] +) @pytest.mark.flaky(reruns=3) -class TestStartApiTerraformApplicationOpenApiAuthorizer(TerraformStartApiIntegrationApplyBase): - terraform_application = "lambda-auth-openapi" - +class TestStartApiTerraformApplicationAuthorizerAfterApply(TerraformStartApiIntegrationApplyBase): def setUp(self): self.url = "http://127.0.0.1:{}".format(self.port) @parameterized.expand( [ - ("/hello", {"headers": {"myheader": "123"}}), - ("/hello-request", {"headers": {"myheader": "123"}, "params": {"mystring": "456"}}), + ("/hello", {"headers": {"myheader": "123"}}, ["v1", "v2"]), + ("/hello-request", {"headers": {"myheader": "123"}, "params": {"mystring": "456"}}, ["v1", "v2"]), ] ) - def test_successful_request(self, endpoint, params): + def test_successful_request(self, endpoint, params, applicable_gateway_versions): + if self.gateway_version not in applicable_gateway_versions: + self.skipTest(f"This test case is not supported for {self.gateway_version} api gateway") response = requests.get(self.url + endpoint, timeout=300, **params) self.assertEqual(response.status_code, 200) @@ -303,11 +379,20 @@ def test_successful_request(self, endpoint, params): @parameterized.expand( [ - ("/hello", {"headers": {"missin": "123"}}), - ("/hello-request", {"headers": {"notcorrect": "123"}, "params": {"abcde": "456"}}), + ("/hello", {"headers": {"missin": "123"}}, ["v1", "v2"]), + ("/hello-request", {"headers": {"notcorrect": "123"}, "params": {"abcde": "456"}}, ["v1", "v2"]), ] ) - def test_missing_identity_sources(self, endpoint, params): + def test_missing_identity_sources(self, endpoint, params, applicable_gateway_versions): + if self.gateway_version not in applicable_gateway_versions: + self.skipTest(f"This test case is not supported for {self.gateway_version} api gateway") response = requests.get(self.url + endpoint, timeout=300, **params) self.assertEqual(response.status_code, 401) + + def test_fails_token_header_validation_authorizer(self): + if self.gateway_version not in ["v1"]: + self.skipTest(f"This test case is not supported for {self.gateway_version} api gateway") + response = requests.get(self.url + "/hello", timeout=300, headers={"myheader": "not valid"}) + + self.assertEqual(response.status_code, 401) diff --git a/tests/integration/local/start_lambda/test_start_lambda_terraform_applications.py b/tests/integration/local/start_lambda/test_start_lambda_terraform_applications.py index 4c4a5f117e..3a24bfbd80 100644 --- a/tests/integration/local/start_lambda/test_start_lambda_terraform_applications.py +++ b/tests/integration/local/start_lambda/test_start_lambda_terraform_applications.py @@ -18,8 +18,6 @@ from docker.errors import APIError from parameterized import parameterized, parameterized_class -from samcli.commands._utils.experimental import EXPERIMENTAL_WARNING -from samcli.lib.utils.colors import Colored from tests.integration.local.common_utils import random_port from tests.integration.local.invoke.layer_utils import LayerUtils from tests.integration.local.start_lambda.start_lambda_api_integ_base import StartLambdaIntegBaseClass @@ -74,7 +72,6 @@ class TestLocalStartLambdaTerraformApplicationWithoutBuild(StartLambdaTerraformA terraform_application = "/testdata/invoke/terraform/simple_application_no_building_logic" template_path = None hook_name = "terraform" - beta_features = True def setUp(self): self.url = "http://127.0.0.1:{}".format(self.port) @@ -118,7 +115,6 @@ class TestLocalStartLambdaTerraformApplicationWithoutBuildCustomPlanFile(StartLa terraform_application = "/testdata/invoke/terraform/simple_application_no_building_logic" template_path = None hook_name = "terraform" - beta_features = True terraform_plan_file = "custom-plan.json" def setUp(self): @@ -175,7 +171,6 @@ class TestLocalStartLambdaTerraformApplicationWithLayersWithoutBuild(StartLambda pre_create_lambda_layers = ["simple_layer1", "simple_layer2", "simple_layer3", "simple_layer33", "simple_layer44"] template_path = None hook_name = "terraform" - beta_features = True @classmethod def setUpClass(cls): @@ -327,7 +322,6 @@ class TestInvalidTerraformApplicationThatReferToS3BucketNotCreatedYet(StartLambd terraform_application = "/testdata/invoke/terraform/invalid_no_local_code_project" template_path = None hook_name = "terraform" - beta_features = True @classmethod def setUpClass(cls): @@ -365,7 +359,6 @@ def test_invoke_function(self): command_list = self.get_start_lambda_command( port=self.port, hook_name=self.hook_name, - beta_features=self.beta_features, ) _, stderr, return_code = self._run_command(command_list, tf_application=self.working_dir) @@ -378,96 +371,6 @@ def test_invoke_function(self): self.assertNotEqual(return_code, 0) -@skipIf( - (not RUN_BY_CANARY and not CI_OVERRIDE), - "Skip Terraform test cases unless running in CI", -) -class TestLocalStartLambdaTerraformApplicationWithExperimentalPromptYes(StartLambdaTerraformApplicationIntegBase): - terraform_application = "/testdata/invoke/terraform/simple_application_no_building_logic" - template_path = None - hook_name = "terraform" - input = b"Y\n" - collect_start_lambda_process_output = True - - def setUp(self): - self.url = "http://127.0.0.1:{}".format(self.port) - self.lambda_client = boto3.client( - "lambda", - endpoint_url=self.url, - region_name="us-east-1", - use_ssl=False, - verify=False, - config=Config(signature_version=UNSIGNED, read_timeout=120, retries={"max_attempts": 0}), - ) - - @pytest.mark.flaky(reruns=3) - def test_invoke_function(self): - response = self.lambda_client.invoke(FunctionName="s3_lambda_function") - - response_body = json.loads(response.get("Payload").read().decode("utf-8")) - expected_response = json.loads('{"statusCode":200,"body":"{\\"message\\": \\"hello world\\"}"}') - - self.assertEqual(response_body, expected_response) - self.assertEqual(response.get("StatusCode"), 200) - - -@skipIf( - (not RUN_BY_CANARY and not CI_OVERRIDE), - "Skip Terraform test cases unless running in CI", -) -class TestLocalStartLambdaTerraformApplicationWithBetaFeatures(StartLambdaTerraformApplicationIntegBase): - terraform_application = "/testdata/invoke/terraform/simple_application_no_building_logic" - template_path = None - hook_name = "terraform" - beta_features = True - collect_start_lambda_process_output = True - - def setUp(self): - self.url = "http://127.0.0.1:{}".format(self.port) - self.lambda_client = boto3.client( - "lambda", - endpoint_url=self.url, - region_name="us-east-1", - use_ssl=False, - verify=False, - config=Config(signature_version=UNSIGNED, read_timeout=120, retries={"max_attempts": 0}), - ) - - @pytest.mark.flaky(reruns=3) - def test_invoke_function_and_warning_message_is_printed(self): - self.assertIn(Colored().yellow(EXPERIMENTAL_WARNING), self.start_lambda_process_error) - - -class TestLocalStartLambdaTerraformApplicationWithExperimentalPromptNo(StartLambdaTerraformApplicationIntegBase): - terraform_application = "/testdata/invoke/terraform/simple_application_no_building_logic" - template_path = None - hook_name = "terraform" - input = b"N\n" - collect_start_lambda_process_output = True - - def setUp(self): - self.url = "http://127.0.0.1:{}".format(self.port) - self.lambda_client = boto3.client( - "lambda", - endpoint_url=self.url, - region_name="us-east-1", - use_ssl=False, - verify=False, - config=Config(signature_version=UNSIGNED, read_timeout=120, retries={"max_attempts": 0}), - ) - - @skipIf( - not CI_OVERRIDE, - "Skip Terraform test cases unless running in CI", - ) - @pytest.mark.flaky(reruns=3) - def test_invoke_function(self): - self.assertRegex( - self.start_lambda_process_error, - "Terraform Support beta feature is not enabled.", - ) - - class TestLocalStartLambdaInvalidUsecasesTerraform(StartLambdaTerraformApplicationIntegBase): @classmethod def setUpClass(cls): @@ -497,60 +400,6 @@ def test_invalid_hook_name(self): ) self.assertNotEqual(return_code, 0) - def test_start_lambda_with_no_beta_feature(self): - command_list = self.get_start_lambda_command(hook_name="terraform", beta_features=False) - - _, stderr, return_code = self._run_command(command_list, tf_application=self.working_dir) - - process_stderr = stderr.strip() - self.assertRegex( - process_stderr.decode("utf-8"), - "Terraform Support beta feature is not enabled.", - ) - self.assertEqual(return_code, 0) - - def test_start_lambda_with_no_beta_feature_option_in_samconfig_toml(self): - samconfig_toml_path = Path(self.working_dir).joinpath("samconfig.toml") - samconfig_lines = [ - bytes("version = 0.1" + os.linesep, "utf-8"), - bytes("[default.global.parameters]" + os.linesep, "utf-8"), - bytes("beta_features = false" + os.linesep, "utf-8"), - ] - with open(samconfig_toml_path, "wb") as file: - file.writelines(samconfig_lines) - - command_list = self.get_start_lambda_command(hook_name="terraform") - - _, stderr, return_code = self._run_command(command_list, tf_application=self.working_dir) - - process_stderr = stderr.strip() - self.assertRegex( - process_stderr.decode("utf-8"), - "Terraform Support beta feature is not enabled.", - ) - self.assertEqual(return_code, 0) - # delete the samconfig file - try: - os.remove(samconfig_toml_path) - except (FileNotFoundError, PermissionError): - pass - - def test_start_lambda_with_no_beta_feature_option_in_environment_variables(self): - environment_variables = os.environ.copy() - environment_variables["SAM_CLI_BETA_TERRAFORM_SUPPORT"] = "False" - - command_list = self.get_start_lambda_command(hook_name="terraform") - _, stderr, return_code = self._run_command( - command_list, tf_application=self.working_dir, env=environment_variables - ) - - process_stderr = stderr.strip() - self.assertRegex( - process_stderr.decode("utf-8"), - "Terraform Support beta feature is not enabled.", - ) - self.assertEqual(return_code, 0) - def test_invalid_coexist_parameters(self): command_list = self.get_start_lambda_command(hook_name="terraform", template_path="path/template.yaml") _, stderr, return_code = self._run_command(command_list, tf_application=self.working_dir) @@ -572,7 +421,6 @@ class TestLocalStartLambdaTerraformApplicationWithLocalImageUri(StartLambdaTerra terraform_application = "/testdata/invoke/terraform/image_lambda_function_local_image_uri" template_path = None hook_name = "terraform" - beta_features = True functions = [ "module.image_lambda2.aws_lambda_function.this[0]", "image_lambda2", diff --git a/tests/integration/testdata/buildcmd/terraform/application_outside_root_directory/root_module/input_samconfig.yaml b/tests/integration/testdata/buildcmd/terraform/application_outside_root_directory/root_module/input_samconfig.yaml index 12d6d000fc..b594a3df22 100644 --- a/tests/integration/testdata/buildcmd/terraform/application_outside_root_directory/root_module/input_samconfig.yaml +++ b/tests/integration/testdata/buildcmd/terraform/application_outside_root_directory/root_module/input_samconfig.yaml @@ -2,7 +2,6 @@ version: 0.1 default: global: parameters: - beta_features: true hook_name: terraform build: parameters: diff --git a/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/main.tf b/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/main.tf index 8005fb4e95..33fa9b91a8 100644 --- a/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/main.tf +++ b/tests/integration/testdata/start_api/terraform/lambda-auth-openapi/main.tf @@ -2,8 +2,14 @@ provider "aws" {} data "aws_region" "current" {} +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + resource "aws_api_gateway_authorizer" "header_authorizer" { - name = "header-authorizer-open-api" + name = "header-authorizer-open-api_${random_uuid.unique_id.result}" rest_api_id = aws_api_gateway_rest_api.api.id authorizer_uri = aws_lambda_function.authorizer.invoke_arn authorizer_credentials = aws_iam_role.invocation_role.arn @@ -13,7 +19,7 @@ resource "aws_api_gateway_authorizer" "header_authorizer" { resource "aws_lambda_function" "authorizer" { filename = "lambda-functions.zip" - function_name = "authorizer-open-api" + function_name = "authorizer-open-api_${random_uuid.unique_id.result}" role = aws_iam_role.invocation_role.arn handler = "handlers.auth_handler" runtime = "python3.8" @@ -22,7 +28,7 @@ resource "aws_lambda_function" "authorizer" { resource "aws_lambda_function" "hello_endpoint" { filename = "lambda-functions.zip" - function_name = "hello-lambda-open-api" + function_name = "hello-lambda-open-api_${random_uuid.unique_id.result}" role = aws_iam_role.invocation_role.arn handler = "handlers.hello_handler" runtime = "python3.8" @@ -30,7 +36,7 @@ resource "aws_lambda_function" "hello_endpoint" { } resource "aws_api_gateway_rest_api" "api" { - name = "api-open-api" + name = "api-open-api_${random_uuid.unique_id.result}" body = jsonencode({ swagger = "2.0" info = { @@ -45,6 +51,7 @@ resource "aws_api_gateway_rest_api" "api" { x-amazon-apigateway-authtype = "custom" x-amazon-apigateway-authorizer = { type = "TOKEN" + identityValidationExpression = "^123$" authorizerUri = "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.authorizer.arn}/invocations" } } @@ -92,7 +99,7 @@ resource "aws_api_gateway_rest_api" "api" { } resource "aws_iam_role" "invocation_role" { - name = "iam-lambda-open-api" + name = "iam-lambda-open-api_${random_uuid.unique_id.result}" path = "/" assume_role_policy = <d% zj=rr_mB#73$cT&^d&Rnvd~@F3nr0UsKKwd(1On*(k|vmU$lC-Lv;C$Aq$o-7pq&E{M-q~^XxoMvJEv_l+m@7jTa9;hPw$6Vy`wtz-{!cZE_a|D; zeVDjm-otwV`j)dFmQUoFvQj%^=GxV#jB;WW`yak~>TfKv^`&=a>12=7dUs@}Yn^z} zFu?8)zcdJTqJ=t z?SE6fiLK;bUk3&I4etb<+SUg09kOvSntbo~f?C7+ga0qrO)`tl_p0VQ<7}sSdh&*g z$w~E2-%Miq^?$!E*jEv^Y;xM}6Fw>bvpuG-H<`PiX<4zU?Xji0HFNg=UgQ|py+YGt zRrH$pt#d_Oa|5T`@Htzt>Z0C$pN~`I-W@BlkX^#O$1aogyad~GpBYa?Oit|OG%Q@r zJne+QhMg=wGo;F|=4x6*KUUklYT^|>#^BDNRr{aKXiYwO@2*tqq9%TG^V?6a-djyoq`xwJ&A=1r>d(6HZAx$U$5MgY z6aJ*VtGvDB-}X1=;G`9O{2n_nZGjRH_M`<$cLKnqRg_v-npu>Zo0?ZrtXEP|LQc|U zWD;RU%@?p_4ayfVu%r>hA|aOqc%vDNd% zj=rr_mB#73$cT&^d&Rnvd~@F3nr0UsKKwd(1On*(k|vmU$lC-Lv;C$Aq$o-7pq&E{M-q~^XxoMvJEv_l+m@7jTa9;hPw$6Vy`wtz-{!cZE_a|D; zeVDjm-otwV`j)dFmQUoFvQj%^=GxV#jB;WW`yak~>TfKv^`&=a>12=7dUs@}Yn^z} zFu?8)zcdJTqJ=t z?SE6fiLK;bUk3&I4etb<+SUg09kOvSntbo~f?C7+ga0qrO)`tl_p0VQ<7}sSdh&*g z$w~E2-%Miq^?$!E*jEv^Y;xM}6Fw>bvpuG-H<`PiX<4zU?Xji0HFNg=UgQ|py+YGt zRrH$pt#d_Oa|5T`@Htzt>Z0C$pN~`I-W@BlkX^#O$1aogyad~GpBYa?Oit|OG%Q@r zJne+QhMg=wGo;F|=4x6*KUUklYT^|>#^BDNRr{aKXiYwO@2*tqq9%TG^V?6a-djyoq`xwJ&A=1r>d(6HZAx$U$5MgY z6aJ*VtGvDB-}X1=;G`9O{2n_nZGjRH_M`<$cLKnqRg_v-npu>Zo0?ZrtXEP|LQc|U zWD;RU%@?p_4ayfVu%r>hA|aOqc%vDNd% zj=rr_mB#73$cT&^d&Rnvd~@F3nr0UsKKwd(1On*(k|vmU$lC-Lv;C$Aq$o-7pq&E{M-q~^XxoMvJEv_l+m@7jTa9;hPw$6Vy`wtz-{!cZE_a|D; zeVDjm-otwV`j)dFmQUoFvQj%^=GxV#jB;WW`yak~>TfKv^`&=a>12=7dUs@}Yn^z} zFu?8)zcdJTqJ=t z?SE6fiLK;bUk3&I4etb<+SUg09kOvSntbo~f?C7+ga0qrO)`tl_p0VQ<7}sSdh&*g z$w~E2-%Miq^?$!E*jEv^Y;xM}6Fw>bvpuG-H<`PiX<4zU?Xji0HFNg=UgQ|py+YGt zRrH$pt#d_Oa|5T`@Htzt>Z0C$pN~`I-W@BlkX^#O$1aogyad~GpBYa?Oit|OG%Q@r zJne+QhMg=wGo;F|=4x6*KUUklYT^|>#^BDNRr{aKXiYwO@2*tqq9%TG^V?6a-djyoq`xwJ&A=1r>d(6HZAx$U$5MgY z6aJ*VtGvDB-}X1=;G`9O{2n_nZGjRH_M`<$cLKnqRg_v-npu>Zo0?ZrtXEP|LQc|U zWD;RU%@?p_4ayfVu%r>hA|aOqc%vDNd% zj=rr_mB#73$cT&^d&Rnvd~@F3nr0UsKKwd(1On*(k|vmU$lC-Lv;C$Aq$o-7pq&E{M-q~^XxoMvJEv_l+m@7jTa9;hPw$6Vy`wtz-{!cZE_a|D; zeVDjm-otwV`j)dFmQUoFvQj%^=GxV#jB;WW`yak~>TfKv^`&=a>12=7dUs@}Yn^z} zFu?8)zcdJTqJ=t z?SE6fiLK;bUk3&I4etb<+SUg09kOvSntbo~f?C7+ga0qrO)`tl_p0VQ<7}sSdh&*g z$w~E2-%Miq^?$!E*jEv^Y;xM}6Fw>bvpuG-H<`PiX<4zU?Xji0HFNg=UgQ|py+YGt zRrH$pt#d_Oa|5T`@Htzt>Z0C$pN~`I-W@BlkX^#O$1aogyad~GpBYa?Oit|OG%Q@r zJne+QhMg=wGo;F|=4x6*KUUklYT^|>#^BDNRr{aKXiYwO@2*tqq9%TG^V?6a-djyoq`xwJ&A=1r>d(6HZAx$U$5MgY z6aJ*VtGvDB-}X1=;G`9O{2n_nZGjRH_M`<$cLKnqRg_v-npu>Zo0?ZrtXEP|LQc|U zWD;RU%@?p_4ayfVu%r>hA|aOqc%vDNr&d(IyBH(R*hQM3^BrYVvjNVFh` z(Gn&hM2SvZ;mKO>u6Nga_kMZ%zs~xf^JSm4_ottcE*Uu!fa+o}tyFgb{}!(R0005- z09@@n9q&7N`$&2PnIUKalnl}nj*J)Q*Y*p90D$DjKmg#v003C940$l8z${Wpe?4C)dOggtq<8}5SU?B$d`H{2wO2{_A8#8++1$^G@O`ReusPd-n9U915lqX zrZ?!+`EgnsYAy1oGs2%|(;^`%O8q|3yttxuc2`3C5my|6Ph3dC+>-a>)z3eGQS?#? z)$eyZ#_oDzm2K5otrP5-Tz5uIGDu0*5TT9UO=684d~*Z1)7IO3ec7P4Z)Rd*dShxl znzh@n-HE-#k}iSI4sLg;leub`u^@9aE)X$9Pf?6_vAWfr@pkR^`C7fJQ|2Kz#SQCH zA8#J#Rf9wPPze*iW$Sa-^DMXd^+W}qe|35YR~ngo&u|SIZ{o6{HX=G!ka$Nk)gwwjwkv@8;9*P_x9aD*DmSs z?{^-LpD3J({+Ox6M16rq5UIyh>&Gmmoo$UinIbHykS}ukB=3~0Nyr3=B^WEvZ-Pdb zMWtTTej?iuGzM<&8sg%;%o#prXmH?U#I=+JPJ*2LvT%t0kPK88=jiIt+qU82#()iv zI5~wqzVz31;sd*0Q$ihSVm`v>lSmK*nZN3-fNT-*ijI4Gm>?HsfRrst6XA&5Dqqq5Zs5slv{FYZ8fpVZN@Nt=YS~Kes~h=#;hT+HN_2-C&ht>O^!YqFmpEY z54e;>nt66d$TQq_LiDqCg!9RJN6StB86$VWHOmsiizPH@ujlB}Kq~HpcjTh@_?L&?CCNRSsef%e*?=_o?^|k0) z2Q#oD1*pDC%)2)3@d)@Pxr2RTq||Esnn4xmOPZoA3;D?7o(NVdzV62A8c)Y8M$(P~ zv*LW`>aNN9r`Tyq(hd4)R1D#5QkkaNM$Syww8i6R;7xxA?txHyOqRkU-TFZ_Xe}l? z83aJsx)yxdSneyy?k&hZZ4`_8oIux+uR?* z50y#TpcWh0XMgnJU8dXuqL9AyYPVhZYD^v2BS1s^RFS&d2Yu@hgmR6{T1-~sr7v0N zBx?O6K&F&^`nzX>LS3qL77&Z#r_81^q|--npa4C}EGw63bQ-JBhn3T~0~WlQ;BCO$ zZr&+vEfS9q#aB($SzJ@BgbG*dt;6cE{>Xb?p@F_i%CH=+pDkg{9Znj=p$m~_N68|KUpvQQRk6aAABSICr8KN&kxz_XXH>H0FD-u0^jP%m3##re4sQ{%Woo;VoANYgoQUQKjae%!C>OP9j-F#CA&K0DMyIwr8SP|MXMNyWEbo6g4y zU0>0ED#1SAX%i84Tq`dGzZNv;fD2~xFpC$=9Fa*uDCE>FIU#vgucy1YNeqw{jT}QW zgD9R=QAL1}(sUWcax!hsOACqJ0J;>7s5}a>6<>Pf7YuqPe33(|lyAlBk{E=M+oR)y zssN*WBzs}<)o8M)9sg0964h7@|#=?;X#!PgAbfpJ$2G&qZt zb5u}JwGUl8ce8uyP9(n(+Rs_UUb?G~DbRKzm(s=q#&6c0MVKF9)e33bT9Y#+YHBn! zi?g+#^TRuCPq6Y?PY&#yP0QQCxe4gG$8$UeABK7{+C6F->pEfLuZZ_~(0bA%HNt*{ z8G)kRZPcrFNdUupPGsAuhQe}2PB-&&0|#`RZ+z$CuB2)i-fqT{n?qJhB!*K*REwU+ z1H_Il<+4Awv5s4$%nC*T{=m}6!+;$u-l9@3Vqdx&;Dii$qXSWqcRCm04-0vhpGKB7 zk(>-ArA8&@Fbnvo5Q$h~&l-hG?RBn^G}^LQDokFdrl6fH$svS4T11(~RtL6uVv*j6-PxyJM!UR8EX5OIMm>dgzlZ z1UQj3^DhWSR;48p?0F2ZCtOdpt(lOVsRy@tBTpdiTkneK!c`4Jh+4rm!eJx|io~WL z9}3$&atw=mRr&d72R3-8zC*pE;-(7~W|sTT3VY(1<(qcC=O9h10yDdmxtbO9qWpR9 z24yzoOW2p(sM*bshA>c+qTj_yn~-&w=Kiif0zBgxceGe;QL&sdae#gQ1=6$Rchsc8SbgB`|o0d-a>HWmqLWgJaYDG;3C=dB{~%%gewwPM+~9fQgjg0|1tut|M42ToY|!MYIduKh<~g0FmFQPPnm}xfJj!%R}VVvhzF7{Pql& zhUvuD>lpc{qf=Dxe#dZJ1qAlGAsSRT#njlibfh&}*9fKwwKxoKaI?#_8w*TO^qV;^ z5y}o;hc~iy4(=&&(g!V`V{&hivLp0l!vvmCR69oeTrb*p!)FV-n$^rSVZmW`Ej9!?=SG{9LrD z-SxCvR+zeF`N3MZfqSW^QYLKlVeV1Fc=@@^WX1Pj@}_!rf|%p!li-(HYB}wGZ&rO? z(nzJewDW>w9)z3Iv`3bl9dt|_1wL>je)jt`0;^6~-X;EE@?>$nZZngg>F&SfQieUN zujx+opOGYa#=!VP&g#YAxUbbAHAvPfepY5gI$ymTl83a4XLMyP+gmfOHpZ~flbc4m zlvKsGv?6dC0N@vS8vQMk&Rswz^97UI*yyXM8Jb#1N!t7qp7Q@Mo;uiGb#Sq@x82s) zGvB5+k2S)85?RQEFS7%8ba*Is9>`t>bEv6CzR*j2Ms{1`X=yaIm}FNMPijs+F5SW? z@p)zXa|@ISGAAoJ*&?};7e!;QiP5zrg!X6=Q$h{we3*VEBI< j|Mk;f?ft1@Y|2I3|6i9zx?qZ5KA?-~xYz-ZU$1`w>otX# literal 0 HcmV?d00001 diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/main.tf b/tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/main.tf new file mode 100644 index 0000000000..8d6c47d4ef --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/main.tf @@ -0,0 +1,118 @@ +provider "aws" {} + +data "aws_region" "current" {} + +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + +resource "aws_lambda_function" "authorizer" { + filename = "lambda-functions.zip" + function_name = "authorizer-${random_uuid.unique_id.result}" + role = aws_iam_role.invocation_role.arn + handler = "handlers.auth_handler" + runtime = "python3.8" + source_code_hash = filebase64sha256("lambda-functions.zip") +} + +resource "aws_lambda_function" "hello_endpoint" { + filename = "lambda-functions.zip" + function_name = "hello-lambda-open-api-${random_uuid.unique_id.result}" + role = aws_iam_role.invocation_role.arn + handler = "handlers.hello_handler" + runtime = "python3.8" + source_code_hash = filebase64sha256("lambda-functions.zip") +} + +resource "aws_apigatewayv2_api" "api" { + protocol_type = "HTTP" + name = "api-open-api-${random_uuid.unique_id.result}" + body = jsonencode({ + "openapi": "3.0", + "info": { + "title": "HttpApiOpenApi" + }, + "components": { + "securitySchemes": { + "HeaderAuth": { + "type": "apiKey", + "in": "header", + "name": "notused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "authorizerPayloadFormatVersion": "2.0", + "identitySource": "$request.header.myheader", + "authorizerUri": "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.authorizer.arn}/invocations" + } + }, + "RequestAuth": { + "type": "apiKey", + "in": "header", + "name": "notused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "authorizerPayloadFormatVersion": "2.0", + "identitySource": "$request.header.myheader, $request.querystring.mystring", + "authorizerUri": "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.authorizer.arn}/invocations" + } + } + } + }, + "paths": { + "/hello": { + "get": { + "security": [ + { + "HeaderAuth": [ + + ] + } + ], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "AWS_PROXY", + "uri": "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.hello_endpoint.arn}/invocations" + } + } + }, + "/hello-request": { + "get": { + "security": [ + { + "RequestAuth": [ + + ] + } + ], + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "AWS_PROXY", + "uri": "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.hello_endpoint.arn}/invocations" + } + } + } + } + }) +} + +resource "aws_iam_role" "invocation_role" { + name = "iam-lambda-open-api_${random_uuid.unique_id.result}" + path = "/" + assume_role_policy = <d% zj=rr_mB#73$cT&^d&Rnvd~@F3nr0UsKKwd(1On*(k|vmU$lC-Lv;C$Aq$o-7pq&E{M-q~^XxoMvJEv_l+m@7jTa9;hPw$6Vy`wtz-{!cZE_a|D; zeVDjm-otwV`j)dFmQUoFvQj%^=GxV#jB;WW`yak~>TfKv^`&=a>12=7dUs@}Yn^z} zFu?8)zcdJTqJ=t z?SE6fiLK;bUk3&I4etb<+SUg09kOvSntbo~f?C7+ga0qrO)`tl_p0VQ<7}sSdh&*g z$w~E2-%Miq^?$!E*jEv^Y;xM}6Fw>bvpuG-H<`PiX<4zU?Xji0HFNg=UgQ|py+YGt zRrH$pt#d_Oa|5T`@Htzt>Z0C$pN~`I-W@BlkX^#O$1aogyad~GpBYa?Oit|OG%Q@r zJne+QhMg=wGo;F|=4x6*KUUklYT^|>#^BDNRr{aKXiYwO@2*tqq9%TG^V?6a-djyoq`xwJ&A=1r>d(6HZAx$U$5MgY z6aJ*VtGvDB-}X1=;G`9O{2n_nZGjRH_M`<$cLKnqRg_v-npu>Zo0?ZrtXEP|LQc|U zWD;RU%@?p_4ayfVu%r>hA|aOqc%vDNr&d(IyBH(R*hQM3^BrYVvjNVFh` z(Gn&hM2SvZ;mKO>u6Nga_kMZ%zs~xf^JSm4_ottcE*Uu!fa+o}tyFgb{}!(R0005- z09@@n9q&7N`$&2PnIUKalnl}nj*J)Q*Y*p90D$DjKmg#v003C940$l8z${Wpe?4C)dOggtq<8}5SU?B$d`H{2wO2{_A8#8++1$^G@O`ReusPd-n9U915lqX zrZ?!+`EgnsYAy1oGs2%|(;^`%O8q|3yttxuc2`3C5my|6Ph3dC+>-a>)z3eGQS?#? z)$eyZ#_oDzm2K5otrP5-Tz5uIGDu0*5TT9UO=684d~*Z1)7IO3ec7P4Z)Rd*dShxl znzh@n-HE-#k}iSI4sLg;leub`u^@9aE)X$9Pf?6_vAWfr@pkR^`C7fJQ|2Kz#SQCH zA8#J#Rf9wPPze*iW$Sa-^DMXd^+W}qe|35YR~ngo&u|SIZ{o6{HX=G!ka$Nk)gwwjwkv@8;9*P_x9aD*DmSs z?{^-LpD3J({+Ox6M16rq5UIyh>&Gmmoo$UinIbHykS}ukB=3~0Nyr3=B^WEvZ-Pdb zMWtTTej?iuGzM<&8sg%;%o#prXmH?U#I=+JPJ*2LvT%t0kPK88=jiIt+qU82#()iv zI5~wqzVz31;sd*0Q$ihSVm`v>lSmK*nZN3-fNT-*ijI4Gm>?HsfRrst6XA&5Dqqq5Zs5slv{FYZ8fpVZN@Nt=YS~Kes~h=#;hT+HN_2-C&ht>O^!YqFmpEY z54e;>nt66d$TQq_LiDqCg!9RJN6StB86$VWHOmsiizPH@ujlB}Kq~HpcjTh@_?L&?CCNRSsef%e*?=_o?^|k0) z2Q#oD1*pDC%)2)3@d)@Pxr2RTq||Esnn4xmOPZoA3;D?7o(NVdzV62A8c)Y8M$(P~ zv*LW`>aNN9r`Tyq(hd4)R1D#5QkkaNM$Syww8i6R;7xxA?txHyOqRkU-TFZ_Xe}l? z83aJsx)yxdSneyy?k&hZZ4`_8oIux+uR?* z50y#TpcWh0XMgnJU8dXuqL9AyYPVhZYD^v2BS1s^RFS&d2Yu@hgmR6{T1-~sr7v0N zBx?O6K&F&^`nzX>LS3qL77&Z#r_81^q|--npa4C}EGw63bQ-JBhn3T~0~WlQ;BCO$ zZr&+vEfS9q#aB($SzJ@BgbG*dt;6cE{>Xb?p@F_i%CH=+pDkg{9Znj=p$m~_N68|KUpvQQRk6aAABSICr8KN&kxz_XXH>H0FD-u0^jP%m3##re4sQ{%Woo;VoANYgoQUQKjae%!C>OP9j-F#CA&K0DMyIwr8SP|MXMNyWEbo6g4y zU0>0ED#1SAX%i84Tq`dGzZNv;fD2~xFpC$=9Fa*uDCE>FIU#vgucy1YNeqw{jT}QW zgD9R=QAL1}(sUWcax!hsOACqJ0J;>7s5}a>6<>Pf7YuqPe33(|lyAlBk{E=M+oR)y zssN*WBzs}<)o8M)9sg0964h7@|#=?;X#!PgAbfpJ$2G&qZt zb5u}JwGUl8ce8uyP9(n(+Rs_UUb?G~DbRKzm(s=q#&6c0MVKF9)e33bT9Y#+YHBn! zi?g+#^TRuCPq6Y?PY&#yP0QQCxe4gG$8$UeABK7{+C6F->pEfLuZZ_~(0bA%HNt*{ z8G)kRZPcrFNdUupPGsAuhQe}2PB-&&0|#`RZ+z$CuB2)i-fqT{n?qJhB!*K*REwU+ z1H_Il<+4Awv5s4$%nC*T{=m}6!+;$u-l9@3Vqdx&;Dii$qXSWqcRCm04-0vhpGKB7 zk(>-ArA8&@Fbnvo5Q$h~&l-hG?RBn^G}^LQDokFdrl6fH$svS4T11(~RtL6uVv*j6-PxyJM!UR8EX5OIMm>dgzlZ z1UQj3^DhWSR;48p?0F2ZCtOdpt(lOVsRy@tBTpdiTkneK!c`4Jh+4rm!eJx|io~WL z9}3$&atw=mRr&d72R3-8zC*pE;-(7~W|sTT3VY(1<(qcC=O9h10yDdmxtbO9qWpR9 z24yzoOW2p(sM*bshA>c+qTj_yn~-&w=Kiif0zBgxceGe;QL&sdae#gQ1=6$Rchsc8SbgB`|o0d-a>HWmqLWgJaYDG;3C=dB{~%%gewwPM+~9fQgjg0|1tut|M42ToY|!MYIduKh<~g0FmFQPPnm}xfJj!%R}VVvhzF7{Pql& zhUvuD>lpc{qf=Dxe#dZJ1qAlGAsSRT#njlibfh&}*9fKwwKxoKaI?#_8w*TO^qV;^ z5y}o;hc~iy4(=&&(g!V`V{&hivLp0l!vvmCR69oeTrb*p!)FV-n$^rSVZmW`Ej9!?=SG{9LrD z-SxCvR+zeF`N3MZfqSW^QYLKlVeV1Fc=@@^WX1Pj@}_!rf|%p!li-(HYB}wGZ&rO? z(nzJewDW>w9)z3Iv`3bl9dt|_1wL>je)jt`0;^6~-X;EE@?>$nZZngg>F&SfQieUN zujx+opOGYa#=!VP&g#YAxUbbAHAvPfepY5gI$ymTl83a4XLMyP+gmfOHpZ~flbc4m zlvKsGv?6dC0N@vS8vQMk&Rswz^97UI*yyXM8Jb#1N!t7qp7Q@Mo;uiGb#Sq@x82s) zGvB5+k2S)85?RQEFS7%8ba*Is9>`t>bEv6CzR*j2Ms{1`X=yaIm}FNMPijs+F5SW? z@p)zXa|@ISGAAoJ*&?};7e!;QiP5zrg!X6=Q$h{we3*VEBI< j|Mk;f?ft1@Y|2I3|6i9zx?qZ5KA?-~xYz-ZU$1`w>otX# literal 0 HcmV?d00001 diff --git a/tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/main.tf b/tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/main.tf new file mode 100644 index 0000000000..3daff8ff59 --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/main.tf @@ -0,0 +1,111 @@ +provider "aws" {} + +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + +resource "aws_lambda_function" "authorizer" { + filename = "lambda-functions.zip" + function_name = "authorizer-${random_uuid.unique_id.result}" + role = aws_iam_role.invocation_role.arn + handler = "handlers.auth_handler" + runtime = "python3.8" + source_code_hash = filebase64sha256("lambda-functions.zip") +} + +resource "aws_lambda_function" "hello_endpoint" { + filename = "lambda-functions.zip" + function_name = "hello_lambda-${random_uuid.unique_id.result}" + role = aws_iam_role.invocation_role.arn + handler = "handlers.hello_handler" + runtime = "python3.8" + source_code_hash = filebase64sha256("lambda-functions.zip") +} + +resource "aws_apigatewayv2_api" "my_api" { + name = "my_api" + protocol_type = "HTTP" +} + +resource "aws_apigatewayv2_integration" "integration" { + api_id = aws_apigatewayv2_api.my_api.id + integration_type = "AWS_PROXY" + integration_method = "POST" + integration_uri = aws_lambda_function.hello_endpoint.invoke_arn + payload_format_version = "2.0" +} + +resource "aws_apigatewayv2_route" "get_hello" { + api_id = aws_apigatewayv2_api.my_api.id + target = "integrations/${aws_apigatewayv2_integration.integration.id}" + route_key = "GET /hello" + operation_name = "get_hello_operation" + authorization_type = "CUSTOM" + authorizer_id = aws_apigatewayv2_authorizer.get_hello.id +} + +resource "aws_apigatewayv2_authorizer" "get_hello" { + api_id = aws_apigatewayv2_api.my_api.id + authorizer_type = "REQUEST" + authorizer_uri = aws_lambda_function.authorizer.invoke_arn + authorizer_credentials_arn = aws_iam_role.invocation_role.arn + authorizer_payload_format_version = "2.0" + identity_sources = ["$request.header.myheader"] + name = "header_authorizer" +} + +resource "aws_apigatewayv2_route" "get_hello_request" { + api_id = aws_apigatewayv2_api.my_api.id + target = "integrations/${aws_apigatewayv2_integration.integration.id}" + route_key = "GET /hello-request" + operation_name = "get_hello_request_operation" + authorization_type = "CUSTOM" + authorizer_id = aws_apigatewayv2_authorizer.get_hello_request.id +} + +resource "aws_apigatewayv2_authorizer" "get_hello_request" { + api_id = aws_apigatewayv2_api.my_api.id + authorizer_type = "REQUEST" + authorizer_uri = aws_lambda_function.authorizer.invoke_arn + authorizer_credentials_arn = aws_iam_role.invocation_role.arn + authorizer_payload_format_version = "2.0" + identity_sources = ["$request.header.myheader", "$request.querystring.mystring"] + name = "request_authorizer" +} + +resource "aws_apigatewayv2_deployment" "example" { + api_id = aws_apigatewayv2_api.my_api.id + depends_on = [ + aws_apigatewayv2_integration.integration, + aws_apigatewayv2_route.get_hello, + aws_apigatewayv2_route.get_hello_request + ] +} + +resource "aws_apigatewayv2_stage" "example" { + api_id = aws_apigatewayv2_api.my_api.id + deployment_id = aws_apigatewayv2_deployment.example.id + name = "example-stage-${random_uuid.unique_id.result}" +} + +resource "aws_iam_role" "invocation_role" { + name = "iam_lambda-${random_uuid.unique_id.result}" + path = "/" + assume_role_policy = < None: self.apigw_authorizer_name = "my_authorizer" self.apigw_integration_response_name = "my_integration_response" + self.apigwv2_api_name = "my_apigwv2_api" + self.apigwv2_api_quick_create_name = "my_apigwv2_api_quick_create" + self.apigwv2_route_name = "my_apigwv2_route" + self.apigwv2_stage_name = "my_apigwv2_stage" + self.apigwv2_integration_name = "my_apigwv2_integration" + self.apigwv2_authorizer_name = "my_authorizer_v2" + self.tf_function_common_properties: dict = { "function_name": self.zip_function_name, "architectures": ["x86_64"], @@ -347,6 +359,26 @@ def setUp(self) -> None: "provider_name": AWS_PROVIDER_NAME, } + self.tf_apigwv2_api_common_attributes: dict = { + "type": "aws_apigatewayv2_api", + "provider_name": AWS_PROVIDER_NAME, + } + + self.tf_apigwv2_route_common_attributes: dict = { + "type": "aws_apigatewayv2_route", + "provider_name": AWS_PROVIDER_NAME, + } + + self.tf_apigwv2_stage_common_attributes: dict = { + "type": "aws_apigatewayv2_stage", + "provider_name": AWS_PROVIDER_NAME, + } + + self.tf_apigwv2_integration_common_attributes: dict = { + "type": "aws_apigatewayv2_integration", + "provider_name": AWS_PROVIDER_NAME, + } + self.tf_lambda_function_resource_common_attributes: dict = { "type": "aws_lambda_function", "provider_name": AWS_PROVIDER_NAME, @@ -762,6 +794,194 @@ def setUp(self) -> None: "Metadata": {"SamResourceId": f"aws_api_gateway_rest_api.{self.apigw_rest_api_name}"}, } + self.tf_apigwv2_api_properties: dict = { + "name": "my_v2_api", + "protocol_type": "HTTP", + "cors_configuration": [ + { + "allow_credentials": True, + "allow_headers": ["Content-Type"], + "allow_methods": ["GET", "OPTIONS", "POST"], + "allow_origins": ["my-origin.com"], + "expose_headers": None, + "max_age": 500, + } + ], + } + + self.expected_cfn_apigwv2_api_properties: dict = { + "Name": "my_v2_api", + "ProtocolType": "HTTP", + "CorsConfiguration": { + "AllowCredentials": True, + "AllowHeaders": ["Content-Type"], + "AllowMethods": ["GET", "OPTIONS", "POST"], + "AllowOrigins": ["my-origin.com"], + "MaxAge": 500, + }, + } + + self.tf_apigwv2_api_resource: dict = { + **self.tf_apigwv2_api_common_attributes, + "values": self.tf_apigwv2_api_properties, + "address": f"aws_apigatewayv2_api.{self.apigwv2_api_name}", + "name": self.apigwv2_api_name, + } + + self.expected_cfn_apigwv2_api: dict = { + "Type": AWS_APIGATEWAY_V2_API, + "Properties": self.expected_cfn_apigwv2_api_properties, + "Metadata": {"SamResourceId": f"aws_apigatewayv2_api.{self.apigwv2_api_name}"}, + } + + self.tf_apigwv2_api_quick_create_properties: dict = { + "name": "my_v2_api_quick_create", + "protocol_type": "HTTP", + "target": "arn:aws:apigateway:{region}:lambda:path/2015-03-31/functions/" + "arn:aws:lambda:{region}:{account-id}:function:{function-name}/invocations", + "route_key": "my_route", + } + + self.expected_cfn_apigwv2_api_quick_create_properties: dict = { + "Name": "my_v2_api_quick_create", + "ProtocolType": "HTTP", + "Target": "arn:aws:apigateway:{region}:lambda:path/2015-03-31/functions/" + "arn:aws:lambda:{region}:{account-id}:function:{function-name}/invocations", + "RouteKey": "my_route", + } + + self.tf_apigwv2_api_quick_create_resource: dict = { + **self.tf_apigwv2_api_common_attributes, + "values": self.tf_apigwv2_api_quick_create_properties, + "address": f"aws_apigatewayv2_api.{self.apigwv2_api_quick_create_name}", + "name": self.apigwv2_api_quick_create_name, + } + + self.expected_cfn_apigwv2_api_quick_create: dict = { + "Type": AWS_APIGATEWAY_V2_API, + "Properties": self.expected_cfn_apigwv2_api_quick_create_properties, + "Metadata": {"SamResourceId": f"aws_apigatewayv2_api.{self.apigwv2_api_quick_create_name}"}, + } + + self.tf_apigwv2_route_properties: dict = { + "api_id": "aws_apigatewayv2_api.my_api.id", + "target": "aws_apigatewayv2_integration.example.id", + "route_key": "$default", + "operation_name": "my_operation", + } + + self.expected_cfn_apigwv2_route_properties: dict = { + "ApiId": "aws_apigatewayv2_api.my_api.id", + "Target": "aws_apigatewayv2_integration.example.id", + "RouteKey": "$default", + "OperationName": "my_operation", + } + + self.tf_apigwv2_route_resource: dict = { + **self.tf_apigwv2_route_common_attributes, + "values": self.tf_apigwv2_route_properties, + "address": f"aws_apigatewayv2_route.{self.apigwv2_route_name}", + "name": self.apigwv2_route_name, + } + + self.expected_cfn_apigwv2_route: dict = { + "Type": AWS_APIGATEWAY_V2_ROUTE, + "Properties": self.expected_cfn_apigwv2_route_properties, + "Metadata": {"SamResourceId": f"aws_apigatewayv2_route.{self.apigwv2_route_name}"}, + } + + self.tf_apigwv2_stage_properties: dict = { + "api_id": "aws_apigatewayv2_api.my_api.id", + "name": "example-stage", + "stage_variables": {"foo": "bar"}, + } + + self.expected_cfn_apigwv2_stage_properties: dict = { + "ApiId": "aws_apigatewayv2_api.my_api.id", + "StageName": "example-stage", + "StageVariables": {"foo": "bar"}, + } + + self.tf_apigwv2_stage_resource: dict = { + **self.tf_apigwv2_stage_common_attributes, + "values": self.tf_apigwv2_stage_properties, + "address": f"aws_apigatewayv2_stage.{self.apigwv2_stage_name}", + "name": self.apigwv2_stage_name, + } + + self.expected_cfn_apigwv2_stage: dict = { + "Type": AWS_APIGATEWAY_V2_STAGE, + "Properties": self.expected_cfn_apigwv2_stage_properties, + "Metadata": {"SamResourceId": f"aws_apigatewayv2_stage.{self.apigwv2_stage_name}"}, + } + + self.tf_apigwv2_integration_properties: dict = { + "api_id": "aws_apigatewayv2_api.my_api.id", + "integration_type": "AWS_PROXY", + "integration_method": "POST", + "integration_uri": "aws_lambda_function.HelloWorldFunction.invoke_arn", + "payload_format_version": "2.0", + } + + self.expected_cfn_apigwv2_integration_properties: dict = { + "ApiId": "aws_apigatewayv2_api.my_api.id", + "IntegrationType": "AWS_PROXY", + "IntegrationMethod": "POST", + "IntegrationUri": "aws_lambda_function.HelloWorldFunction.invoke_arn", + "PayloadFormatVersion": "2.0", + } + + self.tf_apigwv2_integration_resource: dict = { + **self.tf_apigwv2_integration_common_attributes, + "values": self.tf_apigwv2_integration_properties, + "address": f"aws_apigatewayv2_integration.{self.apigwv2_integration_name}", + "name": self.apigwv2_integration_name, + } + + self.expected_cfn_apigwv2_integration: dict = { + "Type": AWS_APIGATEWAY_V2_INTEGRATION, + "Properties": self.expected_cfn_apigwv2_integration_properties, + "Metadata": {"SamResourceId": f"aws_apigatewayv2_integration.{self.apigwv2_integration_name}"}, + } + + self.tf_apigwv2_authorizer_common_attributes: dict = { + "type": "aws_apigatewayv2_authorizer", + "provider_name": AWS_PROVIDER_NAME, + } + + self.tf_apigwv2_authorizer_properties: dict = { + "api_id": "aws_apigatewayv2_api.my_api.id", + "authorizer_type": "REQUEST", + "authorizer_uri": "aws_lambda_function.authorizerv2.invoke_arn", + "name": self.apigwv2_authorizer_name, + "authorizer_payload_format_version": "2.0", + "identity_sources": ["$request.header.hello"], + "enable_simple_responses": False, + } + + self.expected_cfn_apigwv2_authorizer_properties: dict = { + "ApiId": "aws_apigatewayv2_api.my_api.id", + "AuthorizerType": "REQUEST", + "AuthorizerUri": "aws_lambda_function.authorizerv2.invoke_arn", + "Name": self.apigwv2_authorizer_name, + "AuthorizerPayloadFormatVersion": "2.0", + "IdentitySource": ["$request.header.hello"], + "EnableSimpleResponses": False, + } + + self.tf_apigwv2_authorizer_resource: dict = { + **self.tf_apigwv2_authorizer_common_attributes, + "values": self.tf_apigwv2_authorizer_properties, + "address": f"aws_apigatewayv2_authorizer.{self.apigwv2_authorizer_name}", + "name": self.apigwv2_authorizer_name, + } + + self.expected_cfn_apigwv2_authorizer: dict = { + "Type": AWS_APIGATEWAY_V2_AUTHORIZER, + "Properties": self.expected_cfn_apigwv2_authorizer_properties, + "Metadata": {"SamResourceId": f"aws_apigatewayv2_authorizer.{self.apigwv2_authorizer_name}"}, + } + self.tf_json_with_root_module_only: dict = { "planned_values": { "root_module": { @@ -777,6 +997,12 @@ def setUp(self) -> None: self.tf_apigw_integration_resource, self.tf_apigw_authorizer_resource, self.tf_apigw_integration_response_resource, + self.tf_apigwv2_api_resource, + self.tf_apigwv2_api_quick_create_resource, + self.tf_apigwv2_route_resource, + self.tf_apigwv2_stage_resource, + self.tf_apigwv2_integration_resource, + self.tf_apigwv2_authorizer_resource, ] } } @@ -793,9 +1019,14 @@ def setUp(self) -> None: f"AwsApiGatewayMethodMyMethod{self.mock_logical_id_hash}": self.expected_cfn_apigw_method, f"AwsApiGatewayMethodMyMethodAuth{self.mock_logical_id_hash}": self.expected_cfn_apigw_method_with_auth, f"AwsApiGatewayAuthorizerMyAuthorizer{self.mock_logical_id_hash}": self.expected_cfn_apigw_authorizer, + f"AwsApigatewayv2ApiMyApigwv2Api{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_api, + f"AwsApigatewayv2ApiMyApigwv2ApiQuickCreate{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_api_quick_create, + f"AwsApigatewayv2RouteMyApigwv2Route{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_route, + f"AwsApigatewayv2StageMyApigwv2Stage{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_stage, + f"AwsApigatewayv2IntegrationMyApigwv2Integration{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_integration, + f"AwsApigatewayv2AuthorizerMyAuthorizerV2{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_authorizer, }, } - self.tf_json_with_root_module_with_sam_metadata_resources: dict = { "planned_values": { "root_module": { diff --git a/tests/unit/hook_packages/terraform/hooks/prepare/test_property_builder.py b/tests/unit/hook_packages/terraform/hooks/prepare/test_property_builder.py index c5d81ff427..6e8cbfdb4a 100644 --- a/tests/unit/hook_packages/terraform/hooks/prepare/test_property_builder.py +++ b/tests/unit/hook_packages/terraform/hooks/prepare/test_property_builder.py @@ -10,6 +10,7 @@ _build_lambda_function_environment_property, _build_lambda_function_image_config_property, _check_image_config_value, + _get_cors_v2_api, ) from samcli.hook_packages.terraform.hooks.prepare.constants import REMOTE_DUMMY_VALUE @@ -214,3 +215,35 @@ def test_get_json_body_invalid(self, invalid_value): result = _get_json_body({"body": invalid_value}, Mock()) self.assertEqual(result, invalid_value) + + @parameterized.expand( + [ + ( + { + "cors_configuration": [ + { + "allow_credentials": True, + "allow_headers": ["Content-Type"], + "allow_methods": ["GET", "OPTIONS", "POST"], + "allow_origins": ["my-origin.com"], + "expose_headers": None, + "max_age": 500, + } + ], + }, + { + "AllowCredentials": True, + "AllowHeaders": ["Content-Type"], + "AllowMethods": ["GET", "OPTIONS", "POST"], + "AllowOrigins": ["my-origin.com"], + "MaxAge": 500, + }, + ), + ({"cors_configuration": None}, None), + ({"cors_configuration": []}, None), + ({"cors_configuration": [{"allow_credentials": True}]}, {"AllowCredentials": True}), + ] + ) + def test_get_cors_v2_api(self, tf_properties, expected): + response = _get_cors_v2_api(tf_properties, Mock()) + self.assertEqual(response, expected) diff --git a/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py b/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py index 11fd230223..dc80b6cbb6 100644 --- a/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py +++ b/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py @@ -12,11 +12,16 @@ TF_AWS_LAMBDA_FUNCTION, TF_AWS_API_GATEWAY_REST_API, TF_AWS_API_GATEWAY_RESOURCE, + TF_AWS_API_GATEWAY_V2_INTEGRATION, + TF_AWS_API_GATEWAY_V2_API, + TF_AWS_API_GATEWAY_V2_AUTHORIZER, ) from samcli.hook_packages.terraform.hooks.prepare.exceptions import ( GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, GatewayAuthorizerToRestApiLocalVariablesLinkingLimitationException, GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException, + GatewayV2AuthorizerToGatewayV2ApiLocalVariablesLinkingLimitationException, + GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, InvalidResourceLinkingException, LocalVariablesLinkingLimitationException, ONE_LAMBDA_LAYER_LINKING_ISSUE_LINK, @@ -25,6 +30,8 @@ OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException, OneGatewayAuthorizerToRestApiLinkingLimitationException, OneGatewayMethodToGatewayAuthorizerLinkingLimitationException, + OneGatewayV2AuthorizerToGatewayV2ApiLinkingLimitationException, + OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException, OneLambdaLayerLinkingLimitationException, FunctionLayerLocalVariablesLinkingLimitationException, OneGatewayResourceToApiGatewayMethodLinkingLimitationException, @@ -47,6 +54,20 @@ GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitationException, OneGatewayResourceToParentResourceLinkingLimitationException, GatewayResourceToParentResourceLocalVariablesLinkingLimitationException, + OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException, + GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException, + OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException, + GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException, + OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException, + GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException, + OneGatewayV2RouteToGatewayV2ApiLinkingLimitationException, + GatewayV2RouteToGatewayV2ApiLocalVariablesLinkingLimitationException, + OneGatewayV2ApiToLambdaFunctionLinkingLimitationException, + GatewayV2ApiToLambdaFunctionLocalVariablesLinkingLimitationException, + OneGatewayV2StageToGatewayV2ApiLinkingLimitationException, + GatewayV2StageToGatewayV2ApiLocalVariablesLinkingLimitationException, + OneGatewayV2RouteToGatewayV2AuthorizerLinkingLimitationException, + GatewayV2RouteToGatewayV2AuthorizerLocalVariablesLinkingLimitationException, ) from samcli.hook_packages.terraform.hooks.prepare.resource_linking import ( @@ -57,6 +78,8 @@ _link_gateway_authorizer_to_rest_api, _link_gateway_method_to_gateway_authorizer, _link_gateway_method_to_gateway_authorizer_call_back, + _link_gateway_v2_authorizer_to_api, + _link_gateway_v2_authorizer_to_lambda_function, _resolve_module_output, _resolve_module_variable, _build_module, @@ -93,6 +116,22 @@ ResourcePairExceptedDestination, _link_gateway_resource_to_parent_resource_call_back, _link_gateway_resources_to_parents, + _link_gateway_v2_route_to_integration, + API_GATEWAY_V2_INTEGRATION_RESOURCE_ADDRESS_PREFIX, + _link_gateway_v2_route_to_integration_callback, + _link_gateway_v2_integration_to_lambda_function_callback, + _link_gateway_v2_integration_to_lambda_function, + _extract_gateway_v2_integration_id_from_route_target_value, + _link_gateway_v2_integration_to_api, + API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + _link_gateway_v2_resource_to_api_callback, + _link_gateway_v2_route_to_api, + _link_gateway_v2_api_to_function, + _link_gateway_v2_api_to_function_callback, + _link_gateway_v2_stage_to_api, + _link_gateway_v2_route_to_authorizer_callback, + _link_gateway_v2_route_to_authorizer, + API_GATEWAY_V2_AUTHORIZER_RESOURCE_ADDRESS_PREFIX, ) from samcli.hook_packages.terraform.hooks.prepare.utilities import get_configuration_address from samcli.hook_packages.terraform.hooks.prepare.types import ( @@ -2208,6 +2247,26 @@ def test_link_gateway_integration_to_function_call_back( _link_gateway_method_to_gateway_authorizer_call_back, "Could not link multiple Lambda Authorizers to one Gateway Method", ), + ( + _link_gateway_v2_route_to_integration_callback, + "Could not link multiple Gateway V2 Integrations to one Gateway V2 Route", + ), + ( + _link_gateway_v2_integration_to_lambda_function_callback, + "Could not link multiple lambda functions to one Gateway V2 Integration", + ), + ( + _link_gateway_v2_resource_to_api_callback, + "Could not link multiple Gateway V2 Apis to one Gateway V2 resource", + ), + ( + _link_gateway_v2_api_to_function_callback, + "Could not link a V2 API to more than one Lambda Function resources", + ), + ( + _link_gateway_v2_route_to_authorizer_callback, + "Could not link multiple Gateway V2 Authorizers to one Gateway V2 Route", + ), ] ) def test_linking_callbacks_raises_multiple_reference_exception(self, linking_call_back_method, expected_message): @@ -2222,6 +2281,11 @@ def test_linking_callbacks_raises_multiple_reference_exception(self, linking_cal (_link_gateway_resource_to_gateway_resource_call_back,), (_link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back,), (_link_gateway_method_to_gateway_authorizer_call_back,), + (_link_gateway_v2_route_to_integration_callback,), + (_link_gateway_v2_integration_to_lambda_function_callback,), + (_link_gateway_v2_resource_to_api_callback,), + (_link_gateway_v2_api_to_function_callback,), + (_link_gateway_v2_route_to_authorizer_callback,), ] ) def test_linking_callbacks_skips_empty_references(self, linking_call_back_method): @@ -2499,3 +2563,570 @@ def test_link_gateway_method_to_gateway_authorizer( ) mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @patch( + "samcli.hook_packages.terraform.hooks.prepare.resource_linking._extract_gateway_v2_integration_id_from_route_target_value" + ) + @patch( + "samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_route_to_integration_callback" + ) + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_route_to_gateway_v2_integration( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_route_to_integration_callback, + mock_integration_id_extractor_function, + ): + routes_v2_cfn_resources = Mock() + routes_v2_config_resources = Mock() + integrations_v2_tf_resources = Mock() + + _link_gateway_v2_route_to_integration( + routes_v2_config_resources, routes_v2_cfn_resources, integrations_v2_tf_resources + ) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException, + local_variable_linking_exception=GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=routes_v2_cfn_resources, + source_resource_tf_config=routes_v2_config_resources, + destination_resource_tf=integrations_v2_tf_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_INTEGRATION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="target", + cfn_link_field_name="Target", + cfn_resource_update_call_back_function=mock_link_gateway_v2_route_to_integration_callback, + linking_exceptions=mock_resource_linking_exceptions(), + tf_destination_value_extractor_from_link_field_value_function=mock_integration_id_extractor_function, + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @parameterized.expand( + [ + ( + { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": {"Target": "invoke_arn"}, + }, + [LogicalIdReference(value="FunctionA", resource_type=TF_AWS_LAMBDA_FUNCTION)], + {"Fn::Join": ["/", ["integrations", {"Ref": "FunctionA"}]]}, + ), + ( + { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": {"Target": "invoke_arn"}, + }, + [ExistingResourceReference("invoke_arn")], + "integrations/invoke_arn", + ), + ( + { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": {"Target": "invoke_arn"}, + }, + [ExistingResourceReference("integrations/invoke_arn")], + "integrations/invoke_arn", + ), + ( + { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": {"Target": "invoke_arn"}, + }, + [LogicalIdReference(value="Integration", resource_type=TF_AWS_API_GATEWAY_V2_INTEGRATION)], + {"Fn::Join": ["/", ["integrations", {"Ref": "Integration"}]]}, + ), + ] + ) + def test__link_gateway_v2_route_to_integration_callback(self, input_gateway_v2_route, logical_ids, expected_route): + gateway_resource = deepcopy(input_gateway_v2_route) + _link_gateway_v2_route_to_integration_callback(gateway_resource, logical_ids) + input_gateway_v2_route["Properties"]["Target"] = expected_route + self.assertEqual(gateway_resource, input_gateway_v2_route) + + @patch( + "samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_integration_to_lambda_function_callback" + ) + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_integration_to_lambda_function( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_integration_to_lambda_function_callback, + ): + integrations_v2_cfn_resources = Mock() + integrations_v2_config_resources = Mock() + lambda_function_tf_resources = Mock() + + _link_gateway_v2_integration_to_lambda_function( + integrations_v2_config_resources, integrations_v2_cfn_resources, lambda_function_tf_resources + ) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=integrations_v2_cfn_resources, + source_resource_tf_config=integrations_v2_config_resources, + destination_resource_tf=lambda_function_tf_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="invoke_arn", + ), + ], + terraform_link_field_name="integration_uri", + cfn_link_field_name="IntegrationUri", + cfn_resource_update_call_back_function=mock_link_gateway_v2_integration_to_lambda_function_callback, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @parameterized.expand( + [ + ( + { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": {"IntegrationUri": "invoke_arn"}, + }, + [LogicalIdReference(value="FunctionA", resource_type=TF_AWS_LAMBDA_FUNCTION)], + { + "Fn::Join": [ + "", + [ + "arn:", + {"Ref": "AWS::Partition"}, + ":apigateway:", + {"Ref": "AWS::Region"}, + ":lambda:path/2015-03-31/functions/", + {"Fn::GetAtt": ["FunctionA", "Arn"]}, + "/invocations", + ], + ] + }, + ), + ( + { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": {"IntegrationUri": "invoke_arn"}, + }, + [ExistingResourceReference("invoke_arn")], + "invoke_arn", + ), + ] + ) + def test_link_gateway_v2_integration_to_lambda_function_callback( + self, input_gateway_v2_integration, logical_ids, expected_route + ): + gateway_resource = deepcopy(input_gateway_v2_integration) + _link_gateway_v2_integration_to_lambda_function_callback(gateway_resource, logical_ids) + input_gateway_v2_integration["Properties"]["IntegrationUri"] = expected_route + self.assertEqual(gateway_resource, input_gateway_v2_integration) + + @parameterized.expand( + [ + ("integrations/invokeArn", "invokeArn"), + ("invokeArn", "invokeArn"), + ] + ) + def test_extract_gateway_v2_integration_id_from_route_target_value(self, input_value, expected_output): + output = _extract_gateway_v2_integration_id_from_route_target_value(input_value) + self.assertEqual(output, expected_output) + + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_resource_to_api_callback") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_integration_to_gateway_v2_api( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_integration_to_api_callback, + ): + integrations_v2_cfn_resources = Mock() + integrations_v2_config_resources = Mock() + apis_v2_tf_resources = Mock() + + _link_gateway_v2_integration_to_api( + integrations_v2_config_resources, integrations_v2_cfn_resources, apis_v2_tf_resources + ) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=integrations_v2_cfn_resources, + source_resource_tf_config=integrations_v2_config_resources, + destination_resource_tf=apis_v2_tf_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=mock_link_gateway_v2_integration_to_api_callback, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_resource_to_api_callback") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_route_to_gateway_v2_api( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_route_to_api_callback, + ): + routes_v2_cfn_resources = Mock() + routes_v2_config_resources = Mock() + apis_v2_tf_resources = Mock() + + _link_gateway_v2_route_to_api(routes_v2_config_resources, routes_v2_cfn_resources, apis_v2_tf_resources) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2RouteToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2RouteToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=routes_v2_cfn_resources, + source_resource_tf_config=routes_v2_config_resources, + destination_resource_tf=apis_v2_tf_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=mock_link_gateway_v2_route_to_api_callback, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_resource_to_api_callback") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_stage_to_gateway_v2_api( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_stage_to_api_callback, + ): + stages_v2_cfn_resources = Mock() + stages_v2_config_resources = Mock() + apis_v2_tf_resources = Mock() + + _link_gateway_v2_stage_to_api(stages_v2_config_resources, stages_v2_cfn_resources, apis_v2_tf_resources) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2StageToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2StageToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=stages_v2_cfn_resources, + source_resource_tf_config=stages_v2_config_resources, + destination_resource_tf=apis_v2_tf_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=mock_link_gateway_v2_stage_to_api_callback, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @parameterized.expand( + [ + ( + { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": {"ApiId": "api_id"}, + }, + [LogicalIdReference(value="myapi", resource_type=TF_AWS_API_GATEWAY_V2_API)], + {"Ref": "myapi"}, + ), + ( + { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": {"ApiId": "api_id"}, + }, + [ExistingResourceReference("myapi_arn")], + "myapi_arn", + ), + ] + ) + def test_link_gateway_v2_integration_to_api_callback( + self, input_gateway_v2_integration, logical_ids, expected_api_reference + ): + gateway_resource = deepcopy(input_gateway_v2_integration) + _link_gateway_v2_resource_to_api_callback(gateway_resource, logical_ids) + input_gateway_v2_integration["Properties"]["ApiId"] = expected_api_reference + self.assertEqual(gateway_resource, input_gateway_v2_integration) + + @patch( + "samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_authorizer_to_lambda_function_call_back" + ) + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_authorizer_to_lambda_function( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_authorizer_to_lambda_function_call_back, + ): + v2_authorizer_cfn_resources = Mock() + v2_authorizer_config_resources = Mock() + lambda_function_resources = Mock() + + _link_gateway_v2_authorizer_to_lambda_function( + v2_authorizer_config_resources, v2_authorizer_cfn_resources, lambda_function_resources + ) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=v2_authorizer_cfn_resources, + source_resource_tf_config=v2_authorizer_config_resources, + destination_resource_tf=lambda_function_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="invoke_arn", + ), + ], + terraform_link_field_name="authorizer_uri", + cfn_link_field_name="AuthorizerUri", + cfn_resource_update_call_back_function=mock_link_gateway_authorizer_to_lambda_function_call_back, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_resource_to_api_callback") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_authorizer_to_api( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_resource_to_api_callback, + ): + v2_authorizer_cfn_resources = Mock() + v2_authorizer_config_resources = Mock() + v2_api_resources = Mock() + + _link_gateway_v2_authorizer_to_api( + v2_authorizer_config_resources, v2_authorizer_cfn_resources, v2_api_resources + ) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2AuthorizerToGatewayV2ApiLinkingLimitationException, + local_variable_linking_exception=GatewayV2AuthorizerToGatewayV2ApiLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=v2_authorizer_cfn_resources, + source_resource_tf_config=v2_authorizer_config_resources, + destination_resource_tf=v2_api_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="api_id", + cfn_link_field_name="ApiId", + cfn_resource_update_call_back_function=mock_link_gateway_v2_resource_to_api_callback, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_api_to_function_callback") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_api_to_lambda_function( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_api_to_function_callback, + ): + api_v2_cfn_resources = Mock() + quick_create_resource = TFResource("resource_address", "type", Mock(), {"target": ConstantValue("val")}) + combined_resources = { + "ResourceA": quick_create_resource, + "ResourceB": TFResource("resource_address", "type", Mock(), {"name": ConstantValue("MyAPI")}), + } + expected_quick_create_resource = {"ResourceA": quick_create_resource} + lambda_function_tf_resources = Mock() + + _link_gateway_v2_api_to_function(combined_resources, api_v2_cfn_resources, lambda_function_tf_resources) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2ApiToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayV2ApiToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=api_v2_cfn_resources, + source_resource_tf_config=expected_quick_create_resource, + destination_resource_tf=lambda_function_tf_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="invoke_arn", + ), + ], + terraform_link_field_name="target", + cfn_link_field_name="Target", + cfn_resource_update_call_back_function=mock_link_gateway_v2_api_to_function_callback, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @parameterized.expand( + [ + ( + { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": {"Target": "functionA.invoke_arn"}, + }, + [LogicalIdReference(value="FunctionA", resource_type=TF_AWS_LAMBDA_FUNCTION)], + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionA.Arn}/invocations" + }, + ), + ( + { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": {"Target": "functionA.invoke_arn"}, + }, + [ExistingResourceReference("myapi_arn")], + "myapi_arn", + ), + ] + ) + def test_link_gateway_v2_api_to_function_callback(self, input_gateway_v2_api, logical_ids, expected_api_reference): + gateway_resource = deepcopy(input_gateway_v2_api) + _link_gateway_v2_api_to_function_callback(gateway_resource, logical_ids) + input_gateway_v2_api["Properties"]["Target"] = expected_api_reference + self.assertEqual(gateway_resource, input_gateway_v2_api) + + @patch( + "samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_route_to_authorizer_callback" + ) + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair") + @patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions") + def test_link_gateway_v2_route_to_authorizer( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_v2_route_to_authorizer_callback, + ): + routes_v2_cfn_resources = Mock() + routes_v2_config_resources = Mock() + authorizers_v2_tf_resources = Mock() + + _link_gateway_v2_route_to_authorizer( + routes_v2_config_resources, routes_v2_cfn_resources, authorizers_v2_tf_resources + ) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayV2RouteToGatewayV2AuthorizerLinkingLimitationException, + local_variable_linking_exception=GatewayV2RouteToGatewayV2AuthorizerLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=routes_v2_cfn_resources, + source_resource_tf_config=routes_v2_config_resources, + destination_resource_tf=authorizers_v2_tf_resources, + expected_destinations=[ + ResourcePairExceptedDestination( + terraform_resource_type_prefix=API_GATEWAY_V2_AUTHORIZER_RESOURCE_ADDRESS_PREFIX, + terraform_attribute_name="id", + ), + ], + terraform_link_field_name="authorizer_id", + cfn_link_field_name="AuthorizerId", + cfn_resource_update_call_back_function=mock_link_gateway_v2_route_to_authorizer_callback, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @parameterized.expand( + [ + ( + { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": {"AuthorizerId": "auth_id"}, + }, + [LogicalIdReference(value="my_auth_resource", resource_type=TF_AWS_API_GATEWAY_V2_AUTHORIZER)], + {"Ref": "my_auth_resource"}, + ), + ( + { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": {"AuthorizerId": "auth_id"}, + }, + [ExistingResourceReference("my_auth_id")], + "my_auth_id", + ), + ] + ) + def test_link_gateway_v2_route_to_authorizer_callback( + self, input_gateway_v2_route, logical_ids, expected_authorizer_reference + ): + gateway_resource = deepcopy(input_gateway_v2_route) + _link_gateway_v2_route_to_authorizer_callback(gateway_resource, logical_ids) + input_gateway_v2_route["Properties"]["AuthorizerId"] = expected_authorizer_reference + self.assertEqual(gateway_resource, input_gateway_v2_route) diff --git a/tests/unit/hook_packages/terraform/hooks/prepare/test_translate.py b/tests/unit/hook_packages/terraform/hooks/prepare/test_translate.py index 60d377aa0c..ea5239f72d 100644 --- a/tests/unit/hook_packages/terraform/hooks/prepare/test_translate.py +++ b/tests/unit/hook_packages/terraform/hooks/prepare/test_translate.py @@ -6,6 +6,7 @@ from tests.unit.hook_packages.terraform.hooks.prepare.prepare_base import PrepareHookUnitBase from samcli.hook_packages.terraform.hooks.prepare.property_builder import ( + AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING, AWS_LAMBDA_FUNCTION_PROPERTY_BUILDER_MAPPING, AWS_API_GATEWAY_RESOURCE_PROPERTY_BUILDER_MAPPING, AWS_API_GATEWAY_REST_API_PROPERTY_BUILDER_MAPPING, @@ -14,6 +15,10 @@ AWS_API_GATEWAY_INTEGRATION_PROPERTY_BUILDER_MAPPING, AWS_API_GATEWAY_AUTHORIZER_PROPERTY_BUILDER_MAPPING, AWS_API_GATEWAY_INTEGRATION_RESPONSE_PROPERTY_BUILDER_MAPPING, + AWS_API_GATEWAY_V2_API_PROPERTY_BUILDER_MAPPING, + AWS_API_GATEWAY_V2_ROUTE_PROPERTY_BUILDER_MAPPING, + AWS_API_GATEWAY_V2_STAGE_PROPERTY_BUILDER_MAPPING, + AWS_API_GATEWAY_V2_INTEGRATION_PROPERTY_BUILDER_MAPPING, ) from samcli.hook_packages.terraform.hooks.prepare.constants import ( REMOTE_DUMMY_VALUE, @@ -1157,6 +1162,42 @@ def test_translating_apigw_integration_response_method(self): ) self.assertEqual(translated_cfn_properties, self.expected_internal_apigw_integration_response_properties) + def test_translating_apigwv2_api(self): + translated_cfn_properties = _translate_properties( + self.tf_apigwv2_api_properties, AWS_API_GATEWAY_V2_API_PROPERTY_BUILDER_MAPPING, Mock() + ) + self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_api_properties) + + def test_translating_apigwv2_api_quick_create(self): + translated_cfn_properties = _translate_properties( + self.tf_apigwv2_api_quick_create_properties, AWS_API_GATEWAY_V2_API_PROPERTY_BUILDER_MAPPING, Mock() + ) + self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_api_quick_create_properties) + + def test_translating_apigwv2_route(self): + translated_cfn_properties = _translate_properties( + self.tf_apigwv2_route_properties, AWS_API_GATEWAY_V2_ROUTE_PROPERTY_BUILDER_MAPPING, Mock() + ) + self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_route_properties) + + def test_translating_apigwv2_stage(self): + translated_cfn_properties = _translate_properties( + self.tf_apigwv2_stage_properties, AWS_API_GATEWAY_V2_STAGE_PROPERTY_BUILDER_MAPPING, Mock() + ) + self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_stage_properties) + + def test_translating_apigwv2_integration(self): + translated_cfn_properties = _translate_properties( + self.tf_apigwv2_integration_properties, AWS_API_GATEWAY_V2_INTEGRATION_PROPERTY_BUILDER_MAPPING, Mock() + ) + self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_integration_properties) + + def test_translating_apigwv2_authorizer(self): + translated_cfn_properties = _translate_properties( + self.tf_apigwv2_authorizer_properties, AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING, Mock() + ) + self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_authorizer_properties) + class TestUnresolvableAttributeCheck(TestCase): @patch("samcli.hook_packages.terraform.hooks.prepare.translate.RESOURCE_TRANSLATOR_MAPPING")