diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e90f44dae7..be9393007c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,6 +23,25 @@ updates: reviewers: - aws/serverless-application-experience-sbt open-pull-requests-limit: 10 + groups: + boto: + patterns: + - "boto3" + - "boto3-stubs*" + - "botocore" + - "botocore-stubs" + - "mypy-boto3-*" + types: + patterns: + - "types-*" + pytest: + patterns: + - "pytest-*" + exclude-patterns: + # ignore metadata and json-report since newer versions conflict + # as of writing this + - "pytest-metadata" + - "pytest-json-report" ignore: # Ignored intentionally since we have a GHA that updates to more # completely diff --git a/Makefile b/Makefile index 081f22d6ae..3bc09d645b 100644 --- a/Makefile +++ b/Makefile @@ -52,8 +52,8 @@ black-check: format: black ruff samcli --fix -schema: - python schema/make_schema.py +schema: + python -m schema.make_schema # Verifications to run before sending a pull request pr: init dev schema black-check diff --git a/installer/pyinstaller/build-linux.sh b/installer/pyinstaller/build-linux.sh index 2b728866da..04fac68975 100755 --- a/installer/pyinstaller/build-linux.sh +++ b/installer/pyinstaller/build-linux.sh @@ -54,6 +54,7 @@ rm -rf ./output/aws-sam-cli-src/tests rm -rf ./output/aws-sam-cli-src/designs rm -rf ./output/aws-sam-cli-src/docs rm -rf ./output/aws-sam-cli-src/media +rm -rf ./output/aws-sam-cli-src/schema rm -rf ./output/aws-sam-cli-src/Make.ps1 rm -rf ./output/aws-sam-cli-src/CODEOWNERS rm -rf ./output/aws-sam-cli-src/CODE_OF_CONDUCT.md diff --git a/installer/pyinstaller/build-mac.sh b/installer/pyinstaller/build-mac.sh old mode 100644 new mode 100755 index 7a69a6b0a4..f003274cae --- a/installer/pyinstaller/build-mac.sh +++ b/installer/pyinstaller/build-mac.sh @@ -71,8 +71,21 @@ echo "Copying Source" cp -r ../[!.]* ./src cp -r ./src/* ./output/aws-sam-cli-src -echo "Removing CI Scripts" +echo "Removing CI Scripts and other files/direcories not needed" rm -vf ./output/aws-sam-cli-src/appveyor*.yml +rm -rf ./output/aws-sam-cli-src/tests +rm -rf ./output/aws-sam-cli-src/designs +rm -rf ./output/aws-sam-cli-src/docs +rm -rf ./output/aws-sam-cli-src/media +rm -rf ./output/aws-sam-cli-src/schema +rm -rf ./output/aws-sam-cli-src/Make.ps1 +rm -rf ./output/aws-sam-cli-src/CODEOWNERS +rm -rf ./output/aws-sam-cli-src/CODE_OF_CONDUCT.md +rm -rf ./output/aws-sam-cli-src/CONTRIBUTING.md +rm -rf ./output/aws-sam-cli-src/DESIGN.md +rm -rf ./output/aws-sam-cli-src/Makefile +rm -rf ./output/aws-sam-cli-src/mypy.ini +rm -rf ./output/aws-sam-cli-src/pytest.ini echo "Installing Python" curl "https://www.python.org/ftp/python/${python_version}/Python-${python_version}.tgz" --output python.tgz diff --git a/requirements/base.txt b/requirements/base.txt index 871a01d567..900844f62d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -12,9 +12,9 @@ aws-sam-translator==1.73.0 docker~=6.1.0 dateparser~=1.1 requests~=2.31.0 -aws_lambda_builders==1.36.0 -tomlkit==0.11.8 -watchdog==2.1.2 +aws_lambda_builders==1.37.0 +tomlkit==0.12.1 +watchdog==3.0.0 rich~=13.5.2 pyopenssl~=23.2.0 # Pin to <4.18 to until SAM-T no longer uses RefResolver @@ -32,4 +32,4 @@ tzlocal==5.0.1 cfn-lint~=0.79.7 # Type checking boto3 objects -boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.33 +boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.38 diff --git a/requirements/pyinstaller-build.txt b/requirements/pyinstaller-build.txt index 250e93d4f9..4beb7c2ab2 100644 --- a/requirements/pyinstaller-build.txt +++ b/requirements/pyinstaller-build.txt @@ -1,3 +1,3 @@ # Executable binary builder requirements setuptools==68.1.2 -pyinstaller==5.13.0 +pyinstaller==5.13.2 diff --git a/requirements/reproducible-linux.txt b/requirements/reproducible-linux.txt index b53e9b082d..b4c66308ed 100644 --- a/requirements/reproducible-linux.txt +++ b/requirements/reproducible-linux.txt @@ -15,9 +15,9 @@ attrs==23.1.0 \ # jschema-to-python # jsonschema # sarif-om -aws-lambda-builders==1.36.0 \ - --hash=sha256:00ce31612a62dc8fa1a2dcd087648d125b6274ff8b18799d3ebe5bb4824f3e96 \ - --hash=sha256:b673b13d72ab9a85523e70a0980a8df86868bcf285d1ae1c032bb747bfc5b5d1 +aws-lambda-builders==1.37.0 \ + --hash=sha256:18df6b852e56d6754422d37b7c7a4637cbcc9e91e329955812424aefcc716f24 \ + --hash=sha256:e34e2b11dacb7d177e710edce7521dced3b5ee55595ffaffc058922e029907ab # via aws-sam-cli (setup.py) aws-sam-translator==1.73.0 \ --hash=sha256:bfa7cad3a78f002edeec5e39fd61b616cf84f34f61010c5dc2f7a76845fe7a02 \ @@ -33,25 +33,25 @@ blinker==1.6.2 \ --hash=sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213 \ --hash=sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0 # via flask -boto3==1.28.31 \ - --hash=sha256:2761f3249fe25c3ec1a8cd6b95fca2317747503e6f1d127daf6a3d2cdeb25680 \ - --hash=sha256:dc6d72470f6d8926b8cdc10ee7708d7ccdd36d6313c7aa298bc1cf6bedb8921e +boto3==1.28.38 \ + --hash=sha256:cdb466e51ebe4c99640269d88d5450328271437d58e6ce089690d0485bef6174 \ + --hash=sha256:dae0bc5548b39d8dfcf4167d9a238ab899a0491c11c5e77934db71b3ecf34752 # via # aws-sam-cli (setup.py) # aws-sam-translator -boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.33 \ - --hash=sha256:8c4dfdddb423b85f3f33b5ca2b4c48271e668fd71cb5ffa25644606bbf11d4ae \ - --hash=sha256:99f6e25bf958eb501415307a42bb54ef5634f28a7520d6465c5a74673a58cb83 +boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.38 \ + --hash=sha256:3baa1d11f0cf6a5f15801baab38bb000a5dd7653466be4511ee40a8dcf156f95 \ + --hash=sha256:ced704f94b275f498c1213d6d5fa912dc97286c835d99809ec40388ee44bc622 # via aws-sam-cli (setup.py) -botocore==1.31.34 \ - --hash=sha256:23ba9e3a8b4c0e5966bbe2db62edb27f61e16b846f153f22aefda7b3c05c7942 \ - --hash=sha256:456ef8eb458db35b8643eb10e652ed50750d13e5af431593471b2c705c34b5db +botocore==1.31.38 \ + --hash=sha256:86a4c253bba046f775e07f6585ff6c3d75c21a21c171e5bfcf68bc59f29d7b4c \ + --hash=sha256:b02de7898f0a7de0f6569be1c87046035a974006c31fd641f4b97a8dba1fad21 # via # boto3 # s3transfer -botocore-stubs==1.31.34 \ - --hash=sha256:1e90061fc3abcc19f56464a8a65a1eb8289a521b7d322beff05079ca78d7baf5 \ - --hash=sha256:ec742f86636acf27872200987fe2068ba5fb9266ce04170a90ea26b698e15654 +botocore-stubs==1.31.38 \ + --hash=sha256:379afe7834fcadb9dcd6825a0d36eeb0146301b51541211d609d9cbdd912c6a6 \ + --hash=sha256:66076ec073cfa16f913ccb6e76df06b2080974786040d63a9a8d297459ebcae7 # via boto3-stubs certifi==2023.7.22 \ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ @@ -379,53 +379,53 @@ mpmath==1.3.0 \ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c # via sympy -mypy-boto3-apigateway==1.28.16 \ - --hash=sha256:4193124b6ab5b92de0affdd59f1485a17d68ecefff17b411f73a08ea5d5db92d \ - --hash=sha256:82491d942530ab9dabc4c811e188c6ab546c3e658c5718bab58fb45cadef6d0b +mypy-boto3-apigateway==1.28.36 \ + --hash=sha256:aa2c710c7fcc505f4c0a0c851bf872c1426b948d23314d3bcf27ee09e46c9ba7 \ + --hash=sha256:e460e5b40b28fbe292f842993e7bf3ad514d0073774b30f1c5e137de6abac681 # via boto3-stubs -mypy-boto3-cloudformation==1.28.19 \ - --hash=sha256:aadf78eb2f2e3b2e83a4844a80d0c5d0d72ad11c453a11efdd28b0c309b05bf6 \ - --hash=sha256:efb08a2a6d7c744d0d8d60f04514c531355aa7972b53f025d9e08e3adf3a5504 +mypy-boto3-cloudformation==1.28.36 \ + --hash=sha256:0b0bfabd65aaeb38a6206ee592b7cdd0b0baafdf07f9f77afc089868817edd6f \ + --hash=sha256:0e1eeca0f44b8907ba9acd2547f5f68fe4ac6c3b226432561d189cca94544686 # via boto3-stubs -mypy-boto3-ecr==1.28.16 \ - --hash=sha256:412aa7e26d20e7502267e5fec4d94f0bc48db0ced24f65d207911fe70585a4cd \ - --hash=sha256:b3252c5eda47ca1e90980e0158410e8a76c8acdfbda0907f545ddfddaea95258 +mypy-boto3-ecr==1.28.36 \ + --hash=sha256:435f81684a35c5a882810145ebd521609cfca1488d92ed038302b189df284451 \ + --hash=sha256:4b5362716c9f16e1bcc645a1a5e25b185c0bf26a53274810add574998401e09a # via boto3-stubs -mypy-boto3-iam==1.28.16 \ - --hash=sha256:1416df5e838be7cc8b65def4721097faf691f8f0c65f58fe417d18b9e6b8de61 \ - --hash=sha256:8ef9ef9a9fabcc1058a46bbd6b1408960a70d72e45a024862676e5a896c446b3 +mypy-boto3-iam==1.28.37 \ + --hash=sha256:39bd5b8b9a48cb47d909d45c13c713c099c2f84719612a0a848d7a0497c6fcf4 \ + --hash=sha256:a5ed8c70c610f2ae7ee4a32ecf87c42824bfeb1e8a8c6347d888c73383d2c61f # via boto3-stubs -mypy-boto3-lambda==1.28.19 \ - --hash=sha256:88582f8ca71bd7a6bbcf8b05155476f0a9dea79630a4da36d367482925241710 \ - --hash=sha256:955b7702f02f2037ba4c058f6dcebfcce50090ac13c9d031a0052fa9136ec59e +mypy-boto3-lambda==1.28.36 \ + --hash=sha256:70498e6ff6bfd60b758553d27fadf691ba169572faca01c2bd457da0b48b9cff \ + --hash=sha256:edb1f49279f7713929a70eaab00cf3d4ba65a10016db636805d022b2eaf14c84 # via boto3-stubs -mypy-boto3-s3==1.28.27 \ - --hash=sha256:f1094344f68d1ffe2b998404e2e4ff9aa4239438692187fa83ad7b734739991c \ - --hash=sha256:f4fdefbfe084c92a6b3d000689e61ab12a985a72b07c5ff157f8a66bcbdb83ba +mypy-boto3-s3==1.28.36 \ + --hash=sha256:44da375fd4d75b1c5ccc26dcd3be48294c7061445efd6d90ebfca43ffebbb3e4 \ + --hash=sha256:d0e90074e4043edf420292397012e37309ff204442a0874d8c969f56546be665 # via boto3-stubs -mypy-boto3-schemas==1.28.16 \ - --hash=sha256:4f4b4fc784452b5186ab31199e4cb5546d314f3fc666cb698c960ba89e33448e \ - --hash=sha256:7898f9624af46be956bd8e1dffc6fc01d6e2141e067444156bddd2c412ef8084 +mypy-boto3-schemas==1.28.36 \ + --hash=sha256:82af1ad64d0c1275c576607920f13dcc7605d6b7e8483dd58aced8395c824d5f \ + --hash=sha256:ec2648ae282cbd5efd4cd0e866d2e0fee0b73bfc4ce8273484e4194582e688d4 # via boto3-stubs -mypy-boto3-secretsmanager==1.28.24 \ - --hash=sha256:13461d8d2891ec0e430437dbb71c0879ee431ddfedb6b21c265878642faeb2a7 \ - --hash=sha256:e224809e28d99c1360bfe6428e8b567bb4a43c38a71263eba0ff4de7fa321142 +mypy-boto3-secretsmanager==1.28.36 \ + --hash=sha256:0b3706a36bea1889fe70f93e7aed68ea5d39e0c727643ab8282ac03391e17a00 \ + --hash=sha256:7e390887d35bd3708d8c0ce9409525dad08053ea8da5fd047f72ec7bcf093fd3 # via boto3-stubs -mypy-boto3-signer==1.28.16 \ - --hash=sha256:78d9bacf72b865221c6d11a946a0d6aad3b14904475c886fafd08cd1dd54c5d7 \ - --hash=sha256:a67206f20189105a24deee995ab130123fa1af75c39c13c17e404ff74b9ec1fc +mypy-boto3-signer==1.28.36 \ + --hash=sha256:152fc0f05eda925e3ac10aa10bff2d2a9f85431e883e8103fc8463d888f347e0 \ + --hash=sha256:e008e2f4bf8023aea207d35a8ae57de9879fba8109d2cf813ddb0ebbf5300e93 # via boto3-stubs -mypy-boto3-stepfunctions==1.28.16 \ - --hash=sha256:8b2b6578ca38dc0f13d73292b92afd7f3cf11a8999a33886d4c4ab6822f0020a \ - --hash=sha256:962c80b1bac778b64c6334df50a3860bf4d0a5d17560fac765e7913615926b2b +mypy-boto3-stepfunctions==1.28.36 \ + --hash=sha256:3b88c34db51ea3158d7944d3d6f5bd67b823d21d9b09e79a7ce37d36c4a69cf2 \ + --hash=sha256:8c794e98abc5ca23ef13e351f46bb849de634baca6f35286e31e58dede40b687 # via boto3-stubs -mypy-boto3-sts==1.28.16 \ - --hash=sha256:7cd388a7451611813730b83c78179759eb5701b849618d82c3ec7e95576bedb9 \ - --hash=sha256:a00bc7f69d8675058db0fb2c39d4d9dbb3a3c14ae1d0d83fbf930a2a0498629a +mypy-boto3-sts==1.28.37 \ + --hash=sha256:24106ff30ecfe7ad0538657bbd00b6009418a5382b323cac46e0e26c1f5d50fb \ + --hash=sha256:54d64ca695ab90a51c68ac1e67ff9eae7ec69f926649e320a3b90ed1ec841a95 # via boto3-stubs -mypy-boto3-xray==1.28.16 \ - --hash=sha256:4bc7dfd46cb71ca6a9d46b47453d59a08ac80c872cc429dacb45a93abd737172 \ - --hash=sha256:6ddd4acccf272bf663522c5fcd31b9b7dacbed4a01c91e44e4e8c0abb2343c0a +mypy-boto3-xray==1.28.36 \ + --hash=sha256:57a4a32fcc0368e5ec6c58d67f7abdc7332bedb7236ef072c157ae21fb44a332 \ + --hash=sha256:fc7dfbd85d78c14bc45a823165c61dd084a36d7700b4935f88ff3a7b8e8dac48 # via boto3-stubs networkx==3.1 \ --hash=sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36 \ @@ -483,9 +483,9 @@ pydantic==1.10.12 \ --hash=sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6 \ --hash=sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d # via aws-sam-translator -pygments==2.15.1 \ - --hash=sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c \ - --hash=sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1 +pygments==2.16.1 \ + --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ + --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 # via rich pyopenssl==23.2.0 \ --hash=sha256:24f0dc5227396b3e831f4c7f602b950a5e9833d292c8e4a2e06b709292806ae2 \ @@ -749,9 +749,9 @@ text-unidecode==1.3 \ --hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 \ --hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93 # via python-slugify -tomlkit==0.11.8 \ - --hash=sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171 \ - --hash=sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3 +tomlkit==0.12.1 \ + --hash=sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86 \ + --hash=sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899 # via aws-sam-cli (setup.py) types-awscrt==0.19.0 \ --hash=sha256:0e31d7ba44e1898af37d224b94d28ffaef19baf89bb18ea2599de9ac0910a07f \ @@ -767,6 +767,19 @@ typing-extensions==4.7.1 \ # via # aws-sam-cli (setup.py) # aws-sam-translator + # boto3-stubs + # mypy-boto3-apigateway + # mypy-boto3-cloudformation + # mypy-boto3-ecr + # mypy-boto3-iam + # mypy-boto3-lambda + # mypy-boto3-s3 + # mypy-boto3-schemas + # mypy-boto3-secretsmanager + # mypy-boto3-signer + # mypy-boto3-stepfunctions + # mypy-boto3-sts + # mypy-boto3-xray # pydantic tzlocal==5.0.1 \ --hash=sha256:46eb99ad4bdb71f3f72b7d24f4267753e240944ecfc16f25d2719ba89827a803 \ @@ -781,24 +794,34 @@ urllib3==1.26.16 \ # botocore # docker # requests -watchdog==2.1.2 \ - --hash=sha256:0237db4d9024859bea27d0efb59fe75eef290833fd988b8ead7a879b0308c2db \ - --hash=sha256:104266a778906ae0e971368d368a65c4cd032a490a9fca5ba0b78c6c7ae11720 \ - --hash=sha256:188145185c08c73c56f1478ccf1f0f0f85101191439679b35b6b100886ce0b39 \ - --hash=sha256:1a62a4671796dc93d1a7262286217d9e75823c63d4c42782912d39a506d30046 \ - --hash=sha256:255a32d44bbbe62e52874ff755e2eefe271b150e0ec240ad7718a62a7a7a73c4 \ - --hash=sha256:3d6405681471ebe0beb3aa083998c4870e48b57f8afdb45ea1b5957cc5cf1014 \ - --hash=sha256:4b219d46d89cfa49af1d73175487c14a318a74cb8c5442603fd13c6a5b418c86 \ - --hash=sha256:581e3548159fe7d2a9f377a1fbcb41bdcee46849cca8ab803c7ac2e5e04ec77c \ - --hash=sha256:58ebb1095ee493008a7789d47dd62e4999505d82be89fc884d473086fccc6ebd \ - --hash=sha256:598d772beeaf9c98d0df946fbabf0c8365dd95ea46a250c224c725fe0c4730bc \ - --hash=sha256:668391e6c32742d76e5be5db6bf95c455fa4b3d11e76a77c13b39bccb3a47a72 \ - --hash=sha256:6ef9fe57162c4c361692620e1d9167574ba1975ee468b24051ca11c9bba6438e \ - --hash=sha256:91387ee2421f30b75f7ff632c9d48f76648e56bf346a7c805c0a34187a93aab4 \ - --hash=sha256:a42e6d652f820b2b94cd03156c62559a2ea68d476476dfcd77d931e7f1012d4a \ - --hash=sha256:a6471517315a8541a943c00b45f1d252e36898a3ae963d2d52509b89a50cb2b9 \ - --hash=sha256:d34ce2261f118ecd57eedeef95fc2a495fc4a40b3ed7b3bf0bd7a8ccc1ab4f8f \ - --hash=sha256:edcd9ef3fd460bb8a98eb1fcf99941e9fd9f275f45f1a82cb1359ec92975d647 +watchdog==3.0.0 \ + --hash=sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a \ + --hash=sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100 \ + --hash=sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8 \ + --hash=sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc \ + --hash=sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae \ + --hash=sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41 \ + --hash=sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0 \ + --hash=sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f \ + --hash=sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c \ + --hash=sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9 \ + --hash=sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3 \ + --hash=sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709 \ + --hash=sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83 \ + --hash=sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759 \ + --hash=sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9 \ + --hash=sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3 \ + --hash=sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7 \ + --hash=sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f \ + --hash=sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346 \ + --hash=sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674 \ + --hash=sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397 \ + --hash=sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96 \ + --hash=sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d \ + --hash=sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a \ + --hash=sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64 \ + --hash=sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44 \ + --hash=sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33 # via aws-sam-cli (setup.py) websocket-client==1.6.1 \ --hash=sha256:c951af98631d24f8df89ab1019fc365f2227c0892f12fd150e935607c79dd0dd \ diff --git a/requirements/reproducible-mac.txt b/requirements/reproducible-mac.txt index cebc34a041..6e87c1b5fe 100644 --- a/requirements/reproducible-mac.txt +++ b/requirements/reproducible-mac.txt @@ -15,9 +15,9 @@ attrs==23.1.0 \ # jschema-to-python # jsonschema # sarif-om -aws-lambda-builders==1.36.0 \ - --hash=sha256:00ce31612a62dc8fa1a2dcd087648d125b6274ff8b18799d3ebe5bb4824f3e96 \ - --hash=sha256:b673b13d72ab9a85523e70a0980a8df86868bcf285d1ae1c032bb747bfc5b5d1 +aws-lambda-builders==1.37.0 \ + --hash=sha256:18df6b852e56d6754422d37b7c7a4637cbcc9e91e329955812424aefcc716f24 \ + --hash=sha256:e34e2b11dacb7d177e710edce7521dced3b5ee55595ffaffc058922e029907ab # via aws-sam-cli (setup.py) aws-sam-translator==1.73.0 \ --hash=sha256:bfa7cad3a78f002edeec5e39fd61b616cf84f34f61010c5dc2f7a76845fe7a02 \ @@ -51,25 +51,25 @@ blinker==1.6.2 \ --hash=sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213 \ --hash=sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0 # via flask -boto3==1.28.31 \ - --hash=sha256:2761f3249fe25c3ec1a8cd6b95fca2317747503e6f1d127daf6a3d2cdeb25680 \ - --hash=sha256:dc6d72470f6d8926b8cdc10ee7708d7ccdd36d6313c7aa298bc1cf6bedb8921e +boto3==1.28.38 \ + --hash=sha256:cdb466e51ebe4c99640269d88d5450328271437d58e6ce089690d0485bef6174 \ + --hash=sha256:dae0bc5548b39d8dfcf4167d9a238ab899a0491c11c5e77934db71b3ecf34752 # via # aws-sam-cli (setup.py) # aws-sam-translator -boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.33 \ - --hash=sha256:8c4dfdddb423b85f3f33b5ca2b4c48271e668fd71cb5ffa25644606bbf11d4ae \ - --hash=sha256:99f6e25bf958eb501415307a42bb54ef5634f28a7520d6465c5a74673a58cb83 +boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.38 \ + --hash=sha256:3baa1d11f0cf6a5f15801baab38bb000a5dd7653466be4511ee40a8dcf156f95 \ + --hash=sha256:ced704f94b275f498c1213d6d5fa912dc97286c835d99809ec40388ee44bc622 # via aws-sam-cli (setup.py) -botocore==1.31.34 \ - --hash=sha256:23ba9e3a8b4c0e5966bbe2db62edb27f61e16b846f153f22aefda7b3c05c7942 \ - --hash=sha256:456ef8eb458db35b8643eb10e652ed50750d13e5af431593471b2c705c34b5db +botocore==1.31.38 \ + --hash=sha256:86a4c253bba046f775e07f6585ff6c3d75c21a21c171e5bfcf68bc59f29d7b4c \ + --hash=sha256:b02de7898f0a7de0f6569be1c87046035a974006c31fd641f4b97a8dba1fad21 # via # boto3 # s3transfer -botocore-stubs==1.31.34 \ - --hash=sha256:1e90061fc3abcc19f56464a8a65a1eb8289a521b7d322beff05079ca78d7baf5 \ - --hash=sha256:ec742f86636acf27872200987fe2068ba5fb9266ce04170a90ea26b698e15654 +botocore-stubs==1.31.38 \ + --hash=sha256:379afe7834fcadb9dcd6825a0d36eeb0146301b51541211d609d9cbdd912c6a6 \ + --hash=sha256:66076ec073cfa16f913ccb6e76df06b2080974786040d63a9a8d297459ebcae7 # via boto3-stubs certifi==2023.7.22 \ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ @@ -405,53 +405,53 @@ mpmath==1.3.0 \ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c # via sympy -mypy-boto3-apigateway==1.28.16 \ - --hash=sha256:4193124b6ab5b92de0affdd59f1485a17d68ecefff17b411f73a08ea5d5db92d \ - --hash=sha256:82491d942530ab9dabc4c811e188c6ab546c3e658c5718bab58fb45cadef6d0b +mypy-boto3-apigateway==1.28.36 \ + --hash=sha256:aa2c710c7fcc505f4c0a0c851bf872c1426b948d23314d3bcf27ee09e46c9ba7 \ + --hash=sha256:e460e5b40b28fbe292f842993e7bf3ad514d0073774b30f1c5e137de6abac681 # via boto3-stubs -mypy-boto3-cloudformation==1.28.19 \ - --hash=sha256:aadf78eb2f2e3b2e83a4844a80d0c5d0d72ad11c453a11efdd28b0c309b05bf6 \ - --hash=sha256:efb08a2a6d7c744d0d8d60f04514c531355aa7972b53f025d9e08e3adf3a5504 +mypy-boto3-cloudformation==1.28.36 \ + --hash=sha256:0b0bfabd65aaeb38a6206ee592b7cdd0b0baafdf07f9f77afc089868817edd6f \ + --hash=sha256:0e1eeca0f44b8907ba9acd2547f5f68fe4ac6c3b226432561d189cca94544686 # via boto3-stubs -mypy-boto3-ecr==1.28.16 \ - --hash=sha256:412aa7e26d20e7502267e5fec4d94f0bc48db0ced24f65d207911fe70585a4cd \ - --hash=sha256:b3252c5eda47ca1e90980e0158410e8a76c8acdfbda0907f545ddfddaea95258 +mypy-boto3-ecr==1.28.36 \ + --hash=sha256:435f81684a35c5a882810145ebd521609cfca1488d92ed038302b189df284451 \ + --hash=sha256:4b5362716c9f16e1bcc645a1a5e25b185c0bf26a53274810add574998401e09a # via boto3-stubs -mypy-boto3-iam==1.28.16 \ - --hash=sha256:1416df5e838be7cc8b65def4721097faf691f8f0c65f58fe417d18b9e6b8de61 \ - --hash=sha256:8ef9ef9a9fabcc1058a46bbd6b1408960a70d72e45a024862676e5a896c446b3 +mypy-boto3-iam==1.28.37 \ + --hash=sha256:39bd5b8b9a48cb47d909d45c13c713c099c2f84719612a0a848d7a0497c6fcf4 \ + --hash=sha256:a5ed8c70c610f2ae7ee4a32ecf87c42824bfeb1e8a8c6347d888c73383d2c61f # via boto3-stubs -mypy-boto3-lambda==1.28.19 \ - --hash=sha256:88582f8ca71bd7a6bbcf8b05155476f0a9dea79630a4da36d367482925241710 \ - --hash=sha256:955b7702f02f2037ba4c058f6dcebfcce50090ac13c9d031a0052fa9136ec59e +mypy-boto3-lambda==1.28.36 \ + --hash=sha256:70498e6ff6bfd60b758553d27fadf691ba169572faca01c2bd457da0b48b9cff \ + --hash=sha256:edb1f49279f7713929a70eaab00cf3d4ba65a10016db636805d022b2eaf14c84 # via boto3-stubs -mypy-boto3-s3==1.28.27 \ - --hash=sha256:f1094344f68d1ffe2b998404e2e4ff9aa4239438692187fa83ad7b734739991c \ - --hash=sha256:f4fdefbfe084c92a6b3d000689e61ab12a985a72b07c5ff157f8a66bcbdb83ba +mypy-boto3-s3==1.28.36 \ + --hash=sha256:44da375fd4d75b1c5ccc26dcd3be48294c7061445efd6d90ebfca43ffebbb3e4 \ + --hash=sha256:d0e90074e4043edf420292397012e37309ff204442a0874d8c969f56546be665 # via boto3-stubs -mypy-boto3-schemas==1.28.16 \ - --hash=sha256:4f4b4fc784452b5186ab31199e4cb5546d314f3fc666cb698c960ba89e33448e \ - --hash=sha256:7898f9624af46be956bd8e1dffc6fc01d6e2141e067444156bddd2c412ef8084 +mypy-boto3-schemas==1.28.36 \ + --hash=sha256:82af1ad64d0c1275c576607920f13dcc7605d6b7e8483dd58aced8395c824d5f \ + --hash=sha256:ec2648ae282cbd5efd4cd0e866d2e0fee0b73bfc4ce8273484e4194582e688d4 # via boto3-stubs -mypy-boto3-secretsmanager==1.28.24 \ - --hash=sha256:13461d8d2891ec0e430437dbb71c0879ee431ddfedb6b21c265878642faeb2a7 \ - --hash=sha256:e224809e28d99c1360bfe6428e8b567bb4a43c38a71263eba0ff4de7fa321142 +mypy-boto3-secretsmanager==1.28.36 \ + --hash=sha256:0b3706a36bea1889fe70f93e7aed68ea5d39e0c727643ab8282ac03391e17a00 \ + --hash=sha256:7e390887d35bd3708d8c0ce9409525dad08053ea8da5fd047f72ec7bcf093fd3 # via boto3-stubs -mypy-boto3-signer==1.28.16 \ - --hash=sha256:78d9bacf72b865221c6d11a946a0d6aad3b14904475c886fafd08cd1dd54c5d7 \ - --hash=sha256:a67206f20189105a24deee995ab130123fa1af75c39c13c17e404ff74b9ec1fc +mypy-boto3-signer==1.28.36 \ + --hash=sha256:152fc0f05eda925e3ac10aa10bff2d2a9f85431e883e8103fc8463d888f347e0 \ + --hash=sha256:e008e2f4bf8023aea207d35a8ae57de9879fba8109d2cf813ddb0ebbf5300e93 # via boto3-stubs -mypy-boto3-stepfunctions==1.28.16 \ - --hash=sha256:8b2b6578ca38dc0f13d73292b92afd7f3cf11a8999a33886d4c4ab6822f0020a \ - --hash=sha256:962c80b1bac778b64c6334df50a3860bf4d0a5d17560fac765e7913615926b2b +mypy-boto3-stepfunctions==1.28.36 \ + --hash=sha256:3b88c34db51ea3158d7944d3d6f5bd67b823d21d9b09e79a7ce37d36c4a69cf2 \ + --hash=sha256:8c794e98abc5ca23ef13e351f46bb849de634baca6f35286e31e58dede40b687 # via boto3-stubs -mypy-boto3-sts==1.28.16 \ - --hash=sha256:7cd388a7451611813730b83c78179759eb5701b849618d82c3ec7e95576bedb9 \ - --hash=sha256:a00bc7f69d8675058db0fb2c39d4d9dbb3a3c14ae1d0d83fbf930a2a0498629a +mypy-boto3-sts==1.28.37 \ + --hash=sha256:24106ff30ecfe7ad0538657bbd00b6009418a5382b323cac46e0e26c1f5d50fb \ + --hash=sha256:54d64ca695ab90a51c68ac1e67ff9eae7ec69f926649e320a3b90ed1ec841a95 # via boto3-stubs -mypy-boto3-xray==1.28.16 \ - --hash=sha256:4bc7dfd46cb71ca6a9d46b47453d59a08ac80c872cc429dacb45a93abd737172 \ - --hash=sha256:6ddd4acccf272bf663522c5fcd31b9b7dacbed4a01c91e44e4e8c0abb2343c0a +mypy-boto3-xray==1.28.36 \ + --hash=sha256:57a4a32fcc0368e5ec6c58d67f7abdc7332bedb7236ef072c157ae21fb44a332 \ + --hash=sha256:fc7dfbd85d78c14bc45a823165c61dd084a36d7700b4935f88ff3a7b8e8dac48 # via boto3-stubs networkx==3.1 \ --hash=sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36 \ @@ -513,9 +513,9 @@ pydantic==1.10.12 \ --hash=sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6 \ --hash=sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d # via aws-sam-translator -pygments==2.15.1 \ - --hash=sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c \ - --hash=sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1 +pygments==2.16.1 \ + --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ + --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 # via rich pyopenssl==23.2.0 \ --hash=sha256:24f0dc5227396b3e831f4c7f602b950a5e9833d292c8e4a2e06b709292806ae2 \ @@ -779,9 +779,9 @@ text-unidecode==1.3 \ --hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 \ --hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93 # via python-slugify -tomlkit==0.11.8 \ - --hash=sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171 \ - --hash=sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3 +tomlkit==0.12.1 \ + --hash=sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86 \ + --hash=sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899 # via aws-sam-cli (setup.py) types-awscrt==0.19.0 \ --hash=sha256:0e31d7ba44e1898af37d224b94d28ffaef19baf89bb18ea2599de9ac0910a07f \ @@ -826,24 +826,34 @@ urllib3==1.26.16 \ # botocore # docker # requests -watchdog==2.1.2 \ - --hash=sha256:0237db4d9024859bea27d0efb59fe75eef290833fd988b8ead7a879b0308c2db \ - --hash=sha256:104266a778906ae0e971368d368a65c4cd032a490a9fca5ba0b78c6c7ae11720 \ - --hash=sha256:188145185c08c73c56f1478ccf1f0f0f85101191439679b35b6b100886ce0b39 \ - --hash=sha256:1a62a4671796dc93d1a7262286217d9e75823c63d4c42782912d39a506d30046 \ - --hash=sha256:255a32d44bbbe62e52874ff755e2eefe271b150e0ec240ad7718a62a7a7a73c4 \ - --hash=sha256:3d6405681471ebe0beb3aa083998c4870e48b57f8afdb45ea1b5957cc5cf1014 \ - --hash=sha256:4b219d46d89cfa49af1d73175487c14a318a74cb8c5442603fd13c6a5b418c86 \ - --hash=sha256:581e3548159fe7d2a9f377a1fbcb41bdcee46849cca8ab803c7ac2e5e04ec77c \ - --hash=sha256:58ebb1095ee493008a7789d47dd62e4999505d82be89fc884d473086fccc6ebd \ - --hash=sha256:598d772beeaf9c98d0df946fbabf0c8365dd95ea46a250c224c725fe0c4730bc \ - --hash=sha256:668391e6c32742d76e5be5db6bf95c455fa4b3d11e76a77c13b39bccb3a47a72 \ - --hash=sha256:6ef9fe57162c4c361692620e1d9167574ba1975ee468b24051ca11c9bba6438e \ - --hash=sha256:91387ee2421f30b75f7ff632c9d48f76648e56bf346a7c805c0a34187a93aab4 \ - --hash=sha256:a42e6d652f820b2b94cd03156c62559a2ea68d476476dfcd77d931e7f1012d4a \ - --hash=sha256:a6471517315a8541a943c00b45f1d252e36898a3ae963d2d52509b89a50cb2b9 \ - --hash=sha256:d34ce2261f118ecd57eedeef95fc2a495fc4a40b3ed7b3bf0bd7a8ccc1ab4f8f \ - --hash=sha256:edcd9ef3fd460bb8a98eb1fcf99941e9fd9f275f45f1a82cb1359ec92975d647 +watchdog==3.0.0 \ + --hash=sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a \ + --hash=sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100 \ + --hash=sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8 \ + --hash=sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc \ + --hash=sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae \ + --hash=sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41 \ + --hash=sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0 \ + --hash=sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f \ + --hash=sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c \ + --hash=sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9 \ + --hash=sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3 \ + --hash=sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709 \ + --hash=sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83 \ + --hash=sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759 \ + --hash=sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9 \ + --hash=sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3 \ + --hash=sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7 \ + --hash=sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f \ + --hash=sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346 \ + --hash=sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674 \ + --hash=sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397 \ + --hash=sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96 \ + --hash=sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d \ + --hash=sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a \ + --hash=sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64 \ + --hash=sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44 \ + --hash=sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33 # via aws-sam-cli (setup.py) websocket-client==1.6.1 \ --hash=sha256:c951af98631d24f8df89ab1019fc365f2227c0892f12fd150e935607c79dd0dd \ diff --git a/requirements/reproducible-win.txt b/requirements/reproducible-win.txt index 84ac057ab2..9bc07712fd 100644 --- a/requirements/reproducible-win.txt +++ b/requirements/reproducible-win.txt @@ -15,9 +15,9 @@ attrs==23.1.0 \ # jschema-to-python # jsonschema # sarif-om -aws-lambda-builders==1.36.0 \ - --hash=sha256:00ce31612a62dc8fa1a2dcd087648d125b6274ff8b18799d3ebe5bb4824f3e96 \ - --hash=sha256:b673b13d72ab9a85523e70a0980a8df86868bcf285d1ae1c032bb747bfc5b5d1 +aws-lambda-builders==1.37.0 \ + --hash=sha256:18df6b852e56d6754422d37b7c7a4637cbcc9e91e329955812424aefcc716f24 \ + --hash=sha256:e34e2b11dacb7d177e710edce7521dced3b5ee55595ffaffc058922e029907ab # via aws-sam-cli (setup.py) aws-sam-translator==1.73.0 \ --hash=sha256:bfa7cad3a78f002edeec5e39fd61b616cf84f34f61010c5dc2f7a76845fe7a02 \ @@ -51,25 +51,25 @@ blinker==1.6.2 \ --hash=sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213 \ --hash=sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0 # via flask -boto3==1.28.31 \ - --hash=sha256:2761f3249fe25c3ec1a8cd6b95fca2317747503e6f1d127daf6a3d2cdeb25680 \ - --hash=sha256:dc6d72470f6d8926b8cdc10ee7708d7ccdd36d6313c7aa298bc1cf6bedb8921e +boto3==1.28.38 \ + --hash=sha256:cdb466e51ebe4c99640269d88d5450328271437d58e6ce089690d0485bef6174 \ + --hash=sha256:dae0bc5548b39d8dfcf4167d9a238ab899a0491c11c5e77934db71b3ecf34752 # via # aws-sam-cli (setup.py) # aws-sam-translator -boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.33 \ - --hash=sha256:8c4dfdddb423b85f3f33b5ca2b4c48271e668fd71cb5ffa25644606bbf11d4ae \ - --hash=sha256:99f6e25bf958eb501415307a42bb54ef5634f28a7520d6465c5a74673a58cb83 +boto3-stubs[apigateway,cloudformation,ecr,iam,lambda,s3,schemas,secretsmanager,signer,stepfunctions,sts,xray]==1.28.38 \ + --hash=sha256:3baa1d11f0cf6a5f15801baab38bb000a5dd7653466be4511ee40a8dcf156f95 \ + --hash=sha256:ced704f94b275f498c1213d6d5fa912dc97286c835d99809ec40388ee44bc622 # via aws-sam-cli (setup.py) -botocore==1.31.34 \ - --hash=sha256:23ba9e3a8b4c0e5966bbe2db62edb27f61e16b846f153f22aefda7b3c05c7942 \ - --hash=sha256:456ef8eb458db35b8643eb10e652ed50750d13e5af431593471b2c705c34b5db +botocore==1.31.38 \ + --hash=sha256:86a4c253bba046f775e07f6585ff6c3d75c21a21c171e5bfcf68bc59f29d7b4c \ + --hash=sha256:b02de7898f0a7de0f6569be1c87046035a974006c31fd641f4b97a8dba1fad21 # via # boto3 # s3transfer -botocore-stubs==1.31.34 \ - --hash=sha256:1e90061fc3abcc19f56464a8a65a1eb8289a521b7d322beff05079ca78d7baf5 \ - --hash=sha256:ec742f86636acf27872200987fe2068ba5fb9266ce04170a90ea26b698e15654 +botocore-stubs==1.31.38 \ + --hash=sha256:379afe7834fcadb9dcd6825a0d36eeb0146301b51541211d609d9cbdd912c6a6 \ + --hash=sha256:66076ec073cfa16f913ccb6e76df06b2080974786040d63a9a8d297459ebcae7 # via boto3-stubs certifi==2023.7.22 \ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ @@ -409,53 +409,53 @@ mpmath==1.3.0 \ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c # via sympy -mypy-boto3-apigateway==1.28.16 \ - --hash=sha256:4193124b6ab5b92de0affdd59f1485a17d68ecefff17b411f73a08ea5d5db92d \ - --hash=sha256:82491d942530ab9dabc4c811e188c6ab546c3e658c5718bab58fb45cadef6d0b +mypy-boto3-apigateway==1.28.36 \ + --hash=sha256:aa2c710c7fcc505f4c0a0c851bf872c1426b948d23314d3bcf27ee09e46c9ba7 \ + --hash=sha256:e460e5b40b28fbe292f842993e7bf3ad514d0073774b30f1c5e137de6abac681 # via boto3-stubs -mypy-boto3-cloudformation==1.28.19 \ - --hash=sha256:aadf78eb2f2e3b2e83a4844a80d0c5d0d72ad11c453a11efdd28b0c309b05bf6 \ - --hash=sha256:efb08a2a6d7c744d0d8d60f04514c531355aa7972b53f025d9e08e3adf3a5504 +mypy-boto3-cloudformation==1.28.36 \ + --hash=sha256:0b0bfabd65aaeb38a6206ee592b7cdd0b0baafdf07f9f77afc089868817edd6f \ + --hash=sha256:0e1eeca0f44b8907ba9acd2547f5f68fe4ac6c3b226432561d189cca94544686 # via boto3-stubs -mypy-boto3-ecr==1.28.16 \ - --hash=sha256:412aa7e26d20e7502267e5fec4d94f0bc48db0ced24f65d207911fe70585a4cd \ - --hash=sha256:b3252c5eda47ca1e90980e0158410e8a76c8acdfbda0907f545ddfddaea95258 +mypy-boto3-ecr==1.28.36 \ + --hash=sha256:435f81684a35c5a882810145ebd521609cfca1488d92ed038302b189df284451 \ + --hash=sha256:4b5362716c9f16e1bcc645a1a5e25b185c0bf26a53274810add574998401e09a # via boto3-stubs -mypy-boto3-iam==1.28.16 \ - --hash=sha256:1416df5e838be7cc8b65def4721097faf691f8f0c65f58fe417d18b9e6b8de61 \ - --hash=sha256:8ef9ef9a9fabcc1058a46bbd6b1408960a70d72e45a024862676e5a896c446b3 +mypy-boto3-iam==1.28.37 \ + --hash=sha256:39bd5b8b9a48cb47d909d45c13c713c099c2f84719612a0a848d7a0497c6fcf4 \ + --hash=sha256:a5ed8c70c610f2ae7ee4a32ecf87c42824bfeb1e8a8c6347d888c73383d2c61f # via boto3-stubs -mypy-boto3-lambda==1.28.19 \ - --hash=sha256:88582f8ca71bd7a6bbcf8b05155476f0a9dea79630a4da36d367482925241710 \ - --hash=sha256:955b7702f02f2037ba4c058f6dcebfcce50090ac13c9d031a0052fa9136ec59e +mypy-boto3-lambda==1.28.36 \ + --hash=sha256:70498e6ff6bfd60b758553d27fadf691ba169572faca01c2bd457da0b48b9cff \ + --hash=sha256:edb1f49279f7713929a70eaab00cf3d4ba65a10016db636805d022b2eaf14c84 # via boto3-stubs -mypy-boto3-s3==1.28.27 \ - --hash=sha256:f1094344f68d1ffe2b998404e2e4ff9aa4239438692187fa83ad7b734739991c \ - --hash=sha256:f4fdefbfe084c92a6b3d000689e61ab12a985a72b07c5ff157f8a66bcbdb83ba +mypy-boto3-s3==1.28.36 \ + --hash=sha256:44da375fd4d75b1c5ccc26dcd3be48294c7061445efd6d90ebfca43ffebbb3e4 \ + --hash=sha256:d0e90074e4043edf420292397012e37309ff204442a0874d8c969f56546be665 # via boto3-stubs -mypy-boto3-schemas==1.28.16 \ - --hash=sha256:4f4b4fc784452b5186ab31199e4cb5546d314f3fc666cb698c960ba89e33448e \ - --hash=sha256:7898f9624af46be956bd8e1dffc6fc01d6e2141e067444156bddd2c412ef8084 +mypy-boto3-schemas==1.28.36 \ + --hash=sha256:82af1ad64d0c1275c576607920f13dcc7605d6b7e8483dd58aced8395c824d5f \ + --hash=sha256:ec2648ae282cbd5efd4cd0e866d2e0fee0b73bfc4ce8273484e4194582e688d4 # via boto3-stubs -mypy-boto3-secretsmanager==1.28.24 \ - --hash=sha256:13461d8d2891ec0e430437dbb71c0879ee431ddfedb6b21c265878642faeb2a7 \ - --hash=sha256:e224809e28d99c1360bfe6428e8b567bb4a43c38a71263eba0ff4de7fa321142 +mypy-boto3-secretsmanager==1.28.36 \ + --hash=sha256:0b3706a36bea1889fe70f93e7aed68ea5d39e0c727643ab8282ac03391e17a00 \ + --hash=sha256:7e390887d35bd3708d8c0ce9409525dad08053ea8da5fd047f72ec7bcf093fd3 # via boto3-stubs -mypy-boto3-signer==1.28.16 \ - --hash=sha256:78d9bacf72b865221c6d11a946a0d6aad3b14904475c886fafd08cd1dd54c5d7 \ - --hash=sha256:a67206f20189105a24deee995ab130123fa1af75c39c13c17e404ff74b9ec1fc +mypy-boto3-signer==1.28.36 \ + --hash=sha256:152fc0f05eda925e3ac10aa10bff2d2a9f85431e883e8103fc8463d888f347e0 \ + --hash=sha256:e008e2f4bf8023aea207d35a8ae57de9879fba8109d2cf813ddb0ebbf5300e93 # via boto3-stubs -mypy-boto3-stepfunctions==1.28.16 \ - --hash=sha256:8b2b6578ca38dc0f13d73292b92afd7f3cf11a8999a33886d4c4ab6822f0020a \ - --hash=sha256:962c80b1bac778b64c6334df50a3860bf4d0a5d17560fac765e7913615926b2b +mypy-boto3-stepfunctions==1.28.36 \ + --hash=sha256:3b88c34db51ea3158d7944d3d6f5bd67b823d21d9b09e79a7ce37d36c4a69cf2 \ + --hash=sha256:8c794e98abc5ca23ef13e351f46bb849de634baca6f35286e31e58dede40b687 # via boto3-stubs -mypy-boto3-sts==1.28.16 \ - --hash=sha256:7cd388a7451611813730b83c78179759eb5701b849618d82c3ec7e95576bedb9 \ - --hash=sha256:a00bc7f69d8675058db0fb2c39d4d9dbb3a3c14ae1d0d83fbf930a2a0498629a +mypy-boto3-sts==1.28.37 \ + --hash=sha256:24106ff30ecfe7ad0538657bbd00b6009418a5382b323cac46e0e26c1f5d50fb \ + --hash=sha256:54d64ca695ab90a51c68ac1e67ff9eae7ec69f926649e320a3b90ed1ec841a95 # via boto3-stubs -mypy-boto3-xray==1.28.16 \ - --hash=sha256:4bc7dfd46cb71ca6a9d46b47453d59a08ac80c872cc429dacb45a93abd737172 \ - --hash=sha256:6ddd4acccf272bf663522c5fcd31b9b7dacbed4a01c91e44e4e8c0abb2343c0a +mypy-boto3-xray==1.28.36 \ + --hash=sha256:57a4a32fcc0368e5ec6c58d67f7abdc7332bedb7236ef072c157ae21fb44a332 \ + --hash=sha256:fc7dfbd85d78c14bc45a823165c61dd084a36d7700b4935f88ff3a7b8e8dac48 # via boto3-stubs networkx==3.1 \ --hash=sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36 \ @@ -517,9 +517,9 @@ pydantic==1.10.12 \ --hash=sha256:ef6c96b2baa2100ec91a4b428f80d8f28a3c9e53568219b6c298c1125572ebc6 \ --hash=sha256:fdbdd1d630195689f325c9ef1a12900524dceb503b00a987663ff4f58669b93d # via aws-sam-translator -pygments==2.15.1 \ - --hash=sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c \ - --hash=sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1 +pygments==2.16.1 \ + --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ + --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 # via rich pyopenssl==23.2.0 \ --hash=sha256:24f0dc5227396b3e831f4c7f602b950a5e9833d292c8e4a2e06b709292806ae2 \ @@ -799,9 +799,9 @@ text-unidecode==1.3 \ --hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 \ --hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93 # via python-slugify -tomlkit==0.11.8 \ - --hash=sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171 \ - --hash=sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3 +tomlkit==0.12.1 \ + --hash=sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86 \ + --hash=sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899 # via aws-sam-cli (setup.py) types-awscrt==0.19.0 \ --hash=sha256:0e31d7ba44e1898af37d224b94d28ffaef19baf89bb18ea2599de9ac0910a07f \ @@ -850,24 +850,34 @@ urllib3==1.26.16 \ # botocore # docker # requests -watchdog==2.1.2 \ - --hash=sha256:0237db4d9024859bea27d0efb59fe75eef290833fd988b8ead7a879b0308c2db \ - --hash=sha256:104266a778906ae0e971368d368a65c4cd032a490a9fca5ba0b78c6c7ae11720 \ - --hash=sha256:188145185c08c73c56f1478ccf1f0f0f85101191439679b35b6b100886ce0b39 \ - --hash=sha256:1a62a4671796dc93d1a7262286217d9e75823c63d4c42782912d39a506d30046 \ - --hash=sha256:255a32d44bbbe62e52874ff755e2eefe271b150e0ec240ad7718a62a7a7a73c4 \ - --hash=sha256:3d6405681471ebe0beb3aa083998c4870e48b57f8afdb45ea1b5957cc5cf1014 \ - --hash=sha256:4b219d46d89cfa49af1d73175487c14a318a74cb8c5442603fd13c6a5b418c86 \ - --hash=sha256:581e3548159fe7d2a9f377a1fbcb41bdcee46849cca8ab803c7ac2e5e04ec77c \ - --hash=sha256:58ebb1095ee493008a7789d47dd62e4999505d82be89fc884d473086fccc6ebd \ - --hash=sha256:598d772beeaf9c98d0df946fbabf0c8365dd95ea46a250c224c725fe0c4730bc \ - --hash=sha256:668391e6c32742d76e5be5db6bf95c455fa4b3d11e76a77c13b39bccb3a47a72 \ - --hash=sha256:6ef9fe57162c4c361692620e1d9167574ba1975ee468b24051ca11c9bba6438e \ - --hash=sha256:91387ee2421f30b75f7ff632c9d48f76648e56bf346a7c805c0a34187a93aab4 \ - --hash=sha256:a42e6d652f820b2b94cd03156c62559a2ea68d476476dfcd77d931e7f1012d4a \ - --hash=sha256:a6471517315a8541a943c00b45f1d252e36898a3ae963d2d52509b89a50cb2b9 \ - --hash=sha256:d34ce2261f118ecd57eedeef95fc2a495fc4a40b3ed7b3bf0bd7a8ccc1ab4f8f \ - --hash=sha256:edcd9ef3fd460bb8a98eb1fcf99941e9fd9f275f45f1a82cb1359ec92975d647 +watchdog==3.0.0 \ + --hash=sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a \ + --hash=sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100 \ + --hash=sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8 \ + --hash=sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc \ + --hash=sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae \ + --hash=sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41 \ + --hash=sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0 \ + --hash=sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f \ + --hash=sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c \ + --hash=sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9 \ + --hash=sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3 \ + --hash=sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709 \ + --hash=sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83 \ + --hash=sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759 \ + --hash=sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9 \ + --hash=sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3 \ + --hash=sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7 \ + --hash=sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f \ + --hash=sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346 \ + --hash=sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674 \ + --hash=sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397 \ + --hash=sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96 \ + --hash=sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d \ + --hash=sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a \ + --hash=sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64 \ + --hash=sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44 \ + --hash=sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33 # via aws-sam-cli (setup.py) websocket-client==1.6.1 \ --hash=sha256:c951af98631d24f8df89ab1019fc365f2227c0892f12fd150e935607c79dd0dd \ diff --git a/samcli/__init__.py b/samcli/__init__.py index a1f7780776..c1b7d78631 100644 --- a/samcli/__init__.py +++ b/samcli/__init__.py @@ -2,4 +2,4 @@ SAM CLI version """ -__version__ = "1.96.0" +__version__ = "1.97.0" diff --git a/samcli/commands/_utils/custom_options/hook_name_option.py b/samcli/commands/_utils/custom_options/hook_name_option.py index eb333b37bd..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 @@ -35,13 +28,19 @@ class HookNameOption(click.Option): def __init__(self, *args, **kwargs): self.hook_name_option_name = "hook_name" + self.help_option_name = "help" self._force_prepare = kwargs.pop("force_prepare", True) self._invalid_coexist_options = kwargs.pop("invalid_coexist_options", []) super().__init__(*args, **kwargs) def handle_parse_result(self, ctx, opts, args): opt_name = self.hook_name_option_name.replace("_", "-") - if self.hook_name_option_name not in opts and self.hook_name_option_name not in ctx.default_map: + if ( + self.hook_name_option_name not in opts + and self.hook_name_option_name not in ctx.default_map + or self.help_option_name in opts + or self.help_option_name in ctx.default_map + ): return super().handle_parse_result(ctx, opts, args) command_name = ctx.command.name if command_name in ["invoke", "start-lambda", "start-api"]: @@ -61,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: @@ -143,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/hook.py b/samcli/hook_packages/terraform/hooks/prepare/hook.py index 0d8c8ce322..2f58b195b6 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/hook.py +++ b/samcli/hook_packages/terraform/hooks/prepare/hook.py @@ -28,12 +28,17 @@ "HookName": "terraform", } +TF_CLOUD_LINK = ( + "https://docs.aws.amazon.com/serverless-application-model/latest" + "/developerguide/gs-terraform-support.html#gs-terraform-support-cloud" +) TF_CLOUD_EXCEPTION_MESSAGE = "Terraform Cloud does not support saving the generated execution plan" TF_CLOUD_HELP_MESSAGE = ( "Terraform Cloud does not currently support generating local plan " "files that AWS SAM CLI uses to parse the Terraform project.\n" "To use AWS SAM CLI with Terraform Cloud applications, provide " - "a plan file using the --terraform-plan-file flag." + "a plan file using the --terraform-plan-file flag.\n\n" + f"For more information, follow the link: {TF_CLOUD_LINK}" ) TF_BLOCKED_ARGUMENTS = [ @@ -227,7 +232,6 @@ def _generate_plan_file(skip_prepare_infra: bool, terraform_application_dir: str ) from e except LoadingPatternError as e: if TF_CLOUD_EXCEPTION_MESSAGE in e.message: - # TODO: Add link to TF Cloud documentation when that is ready raise TerraformCloudException(TF_CLOUD_HELP_MESSAGE) raise PrepareHookException(f"Error occurred when invoking a process:\n{e}") from e 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/<Gateway Ve Integration resource id>` + + 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/samcli/lib/sync/watch_manager.py b/samcli/lib/sync/watch_manager.py index 8c479ee91a..d26df09717 100644 --- a/samcli/lib/sync/watch_manager.py +++ b/samcli/lib/sync/watch_manager.py @@ -7,6 +7,8 @@ from pathlib import Path from typing import TYPE_CHECKING, List, Optional, Set +from watchdog.events import EVENT_TYPE_OPENED, FileSystemEvent + from samcli.lib.providers.exceptions import InvalidTemplateFile, MissingCodeUri, MissingLocalDefinition from samcli.lib.providers.provider import ResourceIdentifier, Stack, get_all_resource_ids from samcli.lib.providers.sam_stack_provider import SamLocalStackProvider @@ -311,7 +313,27 @@ def _on_code_change_wrapper(self, resource_id: ResourceIdentifier) -> OnChangeCa Callback function """ - def on_code_change(_=None): + def on_code_change(event: Optional[FileSystemEvent] = None) -> None: + """ + Custom event handling to create a new sync flow if a file was modified. + + Parameters + ---------- + event: Optional[FileSystemEvent] + The event that triggered the change + """ + if event and event.event_type == EVENT_TYPE_OPENED: + # Ignore all file opened events since this event is + # added in addition to a create or modified event, + # causing an infinite loop of sync flow creations + LOG.debug("Ignoring file system OPENED event") + return + + # sync flow factory should always exist, but guarding just incase + if not self._sync_flow_factory: + LOG.debug("Sync flow factory not defined, skipping trigger") + return + sync_flow = self._sync_flow_factory.create_sync_flow(resource_id) if sync_flow and not self._waiting_infra_sync: self._sync_flow_executor.add_delayed_sync_flow(sync_flow, dedup=True, wait_time=DEFAULT_WAIT_TIME) diff --git a/samcli/lib/utils/file_observer.py b/samcli/lib/utils/file_observer.py index 282aedb97b..a3ce40b440 100644 --- a/samcli/lib/utils/file_observer.py +++ b/samcli/lib/utils/file_observer.py @@ -14,7 +14,13 @@ from docker import DockerClient from docker.errors import ImageNotFound from docker.types import CancellableStream -from watchdog.events import FileSystemEvent, FileSystemEventHandler, PatternMatchingEventHandler +from watchdog.events import ( + EVENT_TYPE_DELETED, + EVENT_TYPE_OPENED, + FileSystemEvent, + FileSystemEventHandler, + PatternMatchingEventHandler, +) from watchdog.observers import Observer from watchdog.observers.api import BaseObserver, ObservedWatch @@ -425,8 +431,8 @@ def __init__(self) -> None: patterns=["*"], ignore_patterns=[], ignore_directories=False ) - self._code_modification_handler.on_modified = self.on_change - self._code_deletion_handler.on_deleted = self.on_change + self._code_modification_handler.on_modified = self.on_change # type: ignore + self._code_deletion_handler.on_deleted = self.on_change # type: ignore self._watch_lock = threading.Lock() self._lock: Lock = threading.Lock() @@ -442,9 +448,13 @@ def on_change(self, event: FileSystemEvent) -> None: Determines that there is a change happened to some file/dir in the observed paths """ with self._watch_lock: + if event.event_type == EVENT_TYPE_OPENED: + LOG.debug("Ignoring file system OPENED event") + return + LOG.debug("a %s change got detected in path %s", event.event_type, event.src_path) for group, _observed_paths in self._observed_paths_per_group.items(): - if event.event_type == "deleted": + if event.event_type == EVENT_TYPE_DELETED: observed_paths = [ path for path in _observed_paths diff --git a/samcli/lib/utils/path_observer.py b/samcli/lib/utils/path_observer.py index 1e78610435..c61cc07cf8 100644 --- a/samcli/lib/utils/path_observer.py +++ b/samcli/lib/utils/path_observer.py @@ -1,12 +1,14 @@ """ HandlerObserver and its helper classes. """ +import logging import re from dataclasses import dataclass from pathlib import Path from typing import Callable, List, Optional from watchdog.events import ( + EVENT_TYPE_OPENED, FileSystemEvent, FileSystemEventHandler, RegexMatchingEventHandler, @@ -14,6 +16,8 @@ from watchdog.observers import Observer from watchdog.observers.api import DEFAULT_OBSERVER_TIMEOUT, ObservedWatch +LOG = logging.getLogger(__name__) + @dataclass class PathHandler: @@ -75,10 +79,20 @@ def __init__(self, observer: "HandlerObserver", initial_watch: ObservedWatch, pa """ self._observer = observer self._path_handler = path_handler - self._watch = initial_watch + self._watch: Optional[ObservedWatch] = initial_watch + + def _on_parent_change(self, event: FileSystemEvent) -> None: + """ + Callback for changes detected in the parent folder - def _on_parent_change(self, _: FileSystemEvent) -> None: - """Callback for changes detected in the parent folder""" + Parameters + ---------- + event: FileSystemEvent + event + """ + if event.event_type == EVENT_TYPE_OPENED: + LOG.debug("Ignoring file system OPENED event.") + return # When folder is being watched but the folder does not exist if self._watch and not self._path_handler.path.exists(): @@ -108,11 +122,11 @@ def get_dir_parent_path_handler(self) -> PathHandler: ignore_directories=False, case_sensitive=True, ) - parent_folder_handler.on_any_event = self._on_parent_change + parent_folder_handler.on_any_event = self._on_parent_change # type: ignore return PathHandler(path=parent_dir_path, event_handler=parent_folder_handler) -class HandlerObserver(Observer): # pylint: disable=too-many-ancestors +class HandlerObserver(Observer): # type: ignore """ Extended WatchDog Observer that takes in a single PathHandler object. """ @@ -152,7 +166,7 @@ def schedule_handler(self, path_handler: PathHandler) -> ObservedWatch: ObservedWatch corresponding to the PathHandler. If static_folder is True, the parent folder watch will be returned instead. """ - watch = self.schedule(path_handler.event_handler, str(path_handler.path), path_handler.recursive) + watch: ObservedWatch = self.schedule(path_handler.event_handler, str(path_handler.path), path_handler.recursive) if path_handler.static_folder: static_wrapper = StaticFolderWrapper(self, watch, path_handler) parent_path_handler = static_wrapper.get_dir_parent_path_handler() diff --git a/samcli/lib/utils/resource_trigger.py b/samcli/lib/utils/resource_trigger.py index c3246d41df..e3714a2292 100644 --- a/samcli/lib/utils/resource_trigger.py +++ b/samcli/lib/utils/resource_trigger.py @@ -138,7 +138,7 @@ def _validator_wrapper(self, event: Optional[FileSystemEvent] = None) -> None: def get_path_handlers(self) -> List[PathHandler]: file_path_handler = ResourceTrigger.get_single_file_path_handler(Path(self._template_file)) - file_path_handler.event_handler.on_any_event = self._validator_wrapper + file_path_handler.event_handler.on_any_event = self._validator_wrapper # type: ignore return [file_path_handler] @@ -246,7 +246,7 @@ def get_path_handlers(self) -> List[PathHandler]: ) dir_path_handler.self_create = self._on_code_change dir_path_handler.self_delete = self._on_code_change - dir_path_handler.event_handler.on_any_event = self._on_code_change + dir_path_handler.event_handler.on_any_event = self._on_code_change # type: ignore return [dir_path_handler] @@ -314,7 +314,7 @@ def get_path_handlers(self) -> List[PathHandler]: ) dir_path_handler.self_create = self._on_code_change dir_path_handler.self_delete = self._on_code_change - dir_path_handler.event_handler.on_any_event = self._on_code_change + dir_path_handler.event_handler.on_any_event = self._on_code_change # type: ignore return [dir_path_handler] @@ -385,5 +385,5 @@ def get_path_handlers(self) -> List[PathHandler]: A single PathHandler for watching the definition file. """ file_path_handler = ResourceTrigger.get_single_file_path_handler(self.base_dir.joinpath(self._definition_file)) - file_path_handler.event_handler.on_any_event = self._validator_wrapper + file_path_handler.event_handler.on_any_event = self._validator_wrapper # type: ignore return [file_path_handler] diff --git a/samcli/local/apigw/local_apigw_service.py b/samcli/local/apigw/local_apigw_service.py index 0044e4c8cc..3a3c834ec0 100644 --- a/samcli/local/apigw/local_apigw_service.py +++ b/samcli/local/apigw/local_apigw_service.py @@ -610,7 +610,9 @@ def _invoke_lambda_function(self, lambda_function_name: str, event: dict) -> str stdout_writer = StreamWriter(stdout, auto_flush=True) self.lambda_runner.invoke(lambda_function_name, event_str, stdout=stdout_writer, stderr=self.stderr) - lambda_response, _ = LambdaOutputParser.get_lambda_output(stdout) + lambda_response, is_lambda_user_error_response = LambdaOutputParser.get_lambda_output(stdout) + if is_lambda_user_error_response: + raise LambdaResponseParseException return lambda_response @@ -721,6 +723,8 @@ def _request_handler(self, **kwargs): endpoint_service_error = ServiceErrorResponses.not_implemented_locally( "Inline code is not supported for sam local commands. Please write your code in a separate file." ) + except LambdaResponseParseException: + endpoint_service_error = ServiceErrorResponses.lambda_body_failure_response() if endpoint_service_error: return endpoint_service_error diff --git a/samcli/local/apigw/service_error_responses.py b/samcli/local/apigw/service_error_responses.py index 689b9172e9..7e772e8638 100644 --- a/samcli/local/apigw/service_error_responses.py +++ b/samcli/local/apigw/service_error_responses.py @@ -10,6 +10,7 @@ class ServiceErrorResponses: _MISSING_LAMBDA_AUTH_IDENTITY_SOURCES = {"message": "Unauthorized"} _LAMBDA_AUTHORIZER_NOT_AUTHORIZED = {"message": "User is not authorized to access this resource"} + HTTP_STATUS_CODE_500 = 500 HTTP_STATUS_CODE_501 = 501 HTTP_STATUS_CODE_502 = 502 HTTP_STATUS_CODE_403 = 403 @@ -53,6 +54,16 @@ def lambda_failure_response(*args): response_data = jsonify(ServiceErrorResponses._LAMBDA_FAILURE) return make_response(response_data, ServiceErrorResponses.HTTP_STATUS_CODE_502) + @staticmethod + def lambda_body_failure_response(*args): + """ + Helper function to create a Lambda Body Failure Response + + :return: A Flask Response + """ + response_data = jsonify(ServiceErrorResponses._LAMBDA_FAILURE) + return make_response(response_data, ServiceErrorResponses.HTTP_STATUS_CODE_500) + @staticmethod def not_implemented_locally(message): """ diff --git a/samcli/local/docker/lambda_image.py b/samcli/local/docker/lambda_image.py index 0ffb70434b..a8272b3c2c 100644 --- a/samcli/local/docker/lambda_image.py +++ b/samcli/local/docker/lambda_image.py @@ -169,6 +169,11 @@ def build(self, runtime, packagetype, image, layers, architecture, stream=None, tag_prefix = f"{runtime_only_number}-" base_image = f"{self._INVOKE_REPO_PREFIX}/{runtime_image_tag}" + # Temporarily add a version tag to the emulation image so that we don't pull a broken image + if platform.system().lower() == "windows" and runtime in [Runtime.go1x.value, Runtime.java8.value]: + LOG.info("Falling back to a previous version of the emulation image") + base_image = f"{base_image}.2023.08.02.10" + if not base_image: raise InvalidIntermediateImageError(f"Invalid PackageType, PackageType needs to be one of [{ZIP}, {IMAGE}]") diff --git a/setup.py b/setup.py index 792fed29f5..d1050860b6 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,7 @@ def read_version(): author_email="aws-sam-developers@amazon.com", url="https://github.com/aws/aws-sam-cli", license="Apache License 2.0", - packages=find_packages(exclude=["tests.*", "tests", "installer.*", "installer"]), + packages=find_packages(exclude=["tests.*", "tests", "installer.*", "installer", "schema.*", "schema"]), keywords="AWS SAM CLI", # Support Python 3.7 or greater python_requires=">=3.7, <=4.0, !=4.0", 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.py b/tests/integration/local/start_api/test_start_api.py index 29392f9820..c43936031d 100644 --- a/tests/integration/local/start_api/test_start_api.py +++ b/tests/integration/local/start_api/test_start_api.py @@ -603,6 +603,15 @@ def test_invalid_v1_lambda_string_response(self): self.assertEqual(response.json(), {"message": "Internal server error"}) self.assertEqual(response.raw.version, 11) + @pytest.mark.flaky(reruns=3) + @pytest.mark.timeout(timeout=600, method="thread") + def test_invalid_lambda_json_body_response(self): + response = requests.get(self.url + "/invalidresponsebody", timeout=300) + + self.assertEqual(response.status_code, 500) + self.assertEqual(response.json(), {"message": "Internal server error"}) + self.assertEqual(response.raw.version, 11) + class TestStartApiWithSwaggerApis(StartApiIntegBaseClass): template_path = "/testdata/start_api/swagger-template.yaml" 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/main.py b/tests/integration/testdata/start_api/main.py index a742f49a4c..4da7d4b753 100644 --- a/tests/integration/testdata/start_api/main.py +++ b/tests/integration/testdata/start_api/main.py @@ -73,6 +73,8 @@ def invalid_v2_respose_returned(event, context): def invalid_hash_response(event, context): return {"foo": "bar"} +def invalid_body_response(event, context): + return {"errorType": "Error", "errorMessage": ""} def base64_response(event, context): diff --git a/tests/integration/testdata/start_api/template-http-api.yaml b/tests/integration/testdata/start_api/template-http-api.yaml index 843a1a9d39..6dca43b6f2 100644 --- a/tests/integration/testdata/start_api/template-http-api.yaml +++ b/tests/integration/testdata/start_api/template-http-api.yaml @@ -204,6 +204,20 @@ Resources: Method: GET Path: /writetostdout + InValidResponseBodyFromLambdaFunction: + Type: AWS::Serverless::Function + Properties: + Handler: main.invalid_body_response + Runtime: python3.9 + CodeUri: . + Timeout: 600 + Events: + InvalidResponseReturned: + Type: HttpApi + Properties: + Method: GET + Path: /invalidresponsebody + ValidV2ResponseHashFromLambdaFunction: Type: AWS::Serverless::Function Properties: 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 = <<EOF { diff --git a/tests/integration/testdata/start_api/terraform/terraform-v1-api-simple/main.tf b/tests/integration/testdata/start_api/terraform/terraform-v1-api-simple/main.tf index 43012c539e..1e5e60d26d 100644 --- a/tests/integration/testdata/start_api/terraform/terraform-v1-api-simple/main.tf +++ b/tests/integration/testdata/start_api/terraform/terraform-v1-api-simple/main.tf @@ -40,7 +40,7 @@ resource "aws_s3_object" "s3_lambda_code" { resource "aws_lambda_layer_version" "MyAwesomeLayer" { filename = "HelloWorldFunction.zip" - layer_name = "MyAwesomeLayer" + layer_name = "MyAwesomeLayer_${random_uuid.unique_id.result}" compatible_runtimes = ["python3.8"] } @@ -49,11 +49,11 @@ resource "aws_lambda_function" "HelloWorldFunction" { s3_key = "s3_lambda_code_key" handler = "app.lambda_handler" runtime = "python3.8" - function_name = "HelloWorldFunction-${random_uuid.unique_id.result}" + function_name = "HelloWorldFunction_${random_uuid.unique_id.result}" timeout = 500 role = aws_iam_role.iam_for_lambda.arn layers = [aws_lambda_layer_version.MyAwesomeLayer.arn] - depends_on = [aws_s3_object.s3_lambda_code] + depends_on = [aws_s3_bucket.lambda_code_bucket, aws_s3_object.s3_lambda_code] } resource "aws_lambda_function" "HelloWorldFunction2" { @@ -61,14 +61,15 @@ resource "aws_lambda_function" "HelloWorldFunction2" { s3_key = "s3_lambda_code_key" handler = "app.lambda_handler" runtime = "python3.8" - function_name = "HelloWorldFunction2-${random_uuid.unique_id.result}" + function_name = "HelloWorldFunction2_${random_uuid.unique_id.result}" timeout = 500 role = aws_iam_role.iam_for_lambda.arn - depends_on = [aws_s3_object.s3_lambda_code] + layers = [aws_lambda_layer_version.MyAwesomeLayer.arn] + depends_on = [aws_s3_bucket.lambda_code_bucket, aws_s3_object.s3_lambda_code] } resource "aws_api_gateway_rest_api" "MyDemoAPI" { - name = "MyDemoAPI" + name = "MyDemoAPI-${random_uuid.unique_id.result}" binary_media_types = [ "utf-8" ] } diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/HelloWorldFunction.zip b/tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/HelloWorldFunction.zip new file mode 100644 index 0000000000..4daa6de02d Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/HelloWorldFunction.zip differ diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/main.tf b/tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/main.tf new file mode 100644 index 0000000000..4c96658ab6 --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/terraform-v2-api-quick-create/main.tf @@ -0,0 +1,57 @@ +provider "aws" { +} + +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + +resource "aws_iam_role" "iam_for_lambda" { + name = "iam_for_lambda_${random_uuid.unique_id.result}" + + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] +} +EOF +} + +resource "aws_s3_bucket" "lambda_code_bucket" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" +} + +resource "aws_s3_object" "s3_lambda_code" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + key = "s3_lambda_code_key" + source = "HelloWorldFunction.zip" + depends_on = [aws_s3_bucket.lambda_code_bucket] +} + +resource "aws_lambda_function" "HelloWorldFunction" { + s3_bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + s3_key = "s3_lambda_code_key" + handler = "app.lambda_handler" + runtime = "python3.8" + function_name = "HelloWorldFunction_${random_uuid.unique_id.result}" + timeout = 500 + role = aws_iam_role.iam_for_lambda.arn + depends_on = [aws_s3_bucket.lambda_code_bucket] +} + +resource "aws_apigatewayv2_api" "quick_create_api" { + name = "quick_create_api_${random_uuid.unique_id.result}" + protocol_type = "HTTP" + target = aws_lambda_function.HelloWorldFunction.invoke_arn + route_key = "GET /hello" +} diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/HelloWorldFunction.zip b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/HelloWorldFunction.zip new file mode 100644 index 0000000000..4daa6de02d Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/HelloWorldFunction.zip differ diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/main.tf b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/main.tf new file mode 100644 index 0000000000..f2edaa9b48 --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-local-resource-link/main.tf @@ -0,0 +1,86 @@ +provider "aws" { +} + +locals { + api_function = aws_lambda_function.HelloWorldFunction.invoke_arn +} + +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + +resource "aws_iam_role" "iam_for_lambda" { + name = "iam_for_lambda_${random_uuid.unique_id.result}" + + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] +} +EOF +} + +resource "aws_s3_bucket" "lambda_code_bucket" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" +} + +resource "aws_s3_object" "s3_lambda_code" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + key = "s3_lambda_code_key" + source = "HelloWorldFunction.zip" + depends_on = [aws_s3_bucket.lambda_code_bucket] +} + +resource "aws_lambda_function" "HelloWorldFunction" { + s3_bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + s3_key = "s3_lambda_code_key" + handler = "app.lambda_handler" + runtime = "python3.8" + function_name = "HelloWorldFunction_${random_uuid.unique_id.result}" + timeout = 500 + role = aws_iam_role.iam_for_lambda.arn + depends_on = [aws_s3_bucket.lambda_code_bucket] +} + +resource "aws_apigatewayv2_api" "my_api" { + name = "my_api_${random_uuid.unique_id.result}" + protocol_type = "HTTP" +} + +resource "aws_apigatewayv2_route" "example" { + api_id = aws_apigatewayv2_api.my_api.id + target = "integrations/${aws_apigatewayv2_integration.example.id}" + route_key = "GET /hello" + operation_name = "my_operation" + depends_on = [aws_apigatewayv2_integration.example] +} + +resource "aws_apigatewayv2_deployment" "example" { + api_id = aws_apigatewayv2_api.my_api.id + depends_on = [aws_apigatewayv2_integration.example, aws_apigatewayv2_route.example] +} + +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_apigatewayv2_integration" "example" { + api_id = aws_apigatewayv2_api.my_api.id + integration_type = "AWS_PROXY" + integration_method = "POST" + integration_uri = local.api_function + payload_format_version = "2.0" +} diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/HelloWorldFunction.zip b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/HelloWorldFunction.zip new file mode 100644 index 0000000000..4daa6de02d Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/HelloWorldFunction.zip differ diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/main.tf b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/main.tf new file mode 100644 index 0000000000..03d3d4652d --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple-multi-resource-link/main.tf @@ -0,0 +1,99 @@ +provider "aws" { +} + +variable "stage" { + type = string + description = "stage" + default = "beta" +} + +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + +resource "aws_iam_role" "iam_for_lambda" { + name = "iam_for_lambda_${random_uuid.unique_id.result}" + + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] +} +EOF +} + +resource "aws_s3_bucket" "lambda_code_bucket" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" +} + +resource "aws_s3_object" "s3_lambda_code" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + key = "s3_lambda_code_key" + source = "HelloWorldFunction.zip" + depends_on = [aws_s3_bucket.lambda_code_bucket] +} + +resource "aws_lambda_function" "HelloWorldFunction" { + s3_bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + s3_key = "s3_lambda_code_key" + handler = "app.lambda_handler" + runtime = "python3.8" + function_name = "HelloWorldFunction_${random_uuid.unique_id.result}" + timeout = 500 + role = aws_iam_role.iam_for_lambda.arn + depends_on = [aws_s3_object.s3_lambda_code] +} + +resource "aws_lambda_function" "HelloWorldFunction2" { + s3_bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + s3_key = "s3_lambda_code_key" + handler = "app.lambda_handler" + runtime = "python3.8" + function_name = "HelloWorldFunction2_${random_uuid.unique_id.result}" + timeout = 500 + role = aws_iam_role.iam_for_lambda.arn + depends_on = [aws_s3_object.s3_lambda_code] +} + +resource "aws_apigatewayv2_api" "my_api" { + name = "my_api_${random_uuid.unique_id.result}" + protocol_type = "HTTP" +} + +resource "aws_apigatewayv2_route" "example" { + api_id = aws_apigatewayv2_api.my_api.id + target = "integrations/${aws_apigatewayv2_integration.example.id}" + route_key = "GET /hello" + operation_name = "my_operation" + depends_on = [aws_apigatewayv2_integration.example] +} + +resource "aws_apigatewayv2_deployment" "example" { + api_id = aws_apigatewayv2_api.my_api.id + depends_on = [aws_apigatewayv2_integration.example, aws_apigatewayv2_route.example] +} + +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_apigatewayv2_integration" "example" { + api_id = aws_apigatewayv2_api.my_api.id + integration_type = "AWS_PROXY" + integration_method = "POST" + integration_uri = var.stage == "beta" ? aws_lambda_function.HelloWorldFunction.invoke_arn : aws_lambda_function.HelloWorldFunction2.invoke_arn + payload_format_version = "2.0" +} diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/HelloWorldFunction.zip b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/HelloWorldFunction.zip new file mode 100644 index 0000000000..4daa6de02d Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/HelloWorldFunction.zip differ diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/main.tf b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/main.tf new file mode 100644 index 0000000000..2fd8f84698 --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/terraform-v2-api-simple/main.tf @@ -0,0 +1,82 @@ +provider "aws" { +} + +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + +resource "aws_iam_role" "iam_for_lambda" { + name = "iam_for_lambda_${random_uuid.unique_id.result}" + + assume_role_policy = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] +} +EOF +} + +resource "aws_s3_bucket" "lambda_code_bucket" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" +} + +resource "aws_s3_object" "s3_lambda_code" { + bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + key = "s3_lambda_code_key" + source = "HelloWorldFunction.zip" + depends_on = [aws_s3_bucket.lambda_code_bucket] +} + +resource "aws_lambda_function" "HelloWorldFunction" { + s3_bucket = "lambda-code-bucket-${random_uuid.unique_id.result}" + s3_key = "s3_lambda_code_key" + handler = "app.lambda_handler" + runtime = "python3.8" + function_name = "HelloWorldFunction_${random_uuid.unique_id.result}" + timeout = 500 + role = aws_iam_role.iam_for_lambda.arn + depends_on = [aws_s3_bucket.lambda_code_bucket] +} + +resource "aws_apigatewayv2_api" "my_api" { + name = "my_api_${random_uuid.unique_id.result}" + protocol_type = "HTTP" +} + +resource "aws_apigatewayv2_route" "example" { + api_id = aws_apigatewayv2_api.my_api.id + target = "integrations/${aws_apigatewayv2_integration.example.id}" + route_key = "GET /hello" + operation_name = "my_operation" + depends_on = [aws_apigatewayv2_integration.example] +} + +resource "aws_apigatewayv2_deployment" "example" { + api_id = aws_apigatewayv2_api.my_api.id + depends_on = [aws_apigatewayv2_integration.example, aws_apigatewayv2_route.example] +} + +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_apigatewayv2_integration" "example" { + 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" +} diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/lambda-functions.zip b/tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/lambda-functions.zip new file mode 100644 index 0000000000..2845ce8129 Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/terraform-v2-auth-openapi/lambda-functions.zip differ 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 = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] +} +EOF +} diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-openapi/lambda-functions.zip b/tests/integration/testdata/start_api/terraform/terraform-v2-openapi/lambda-functions.zip new file mode 100644 index 0000000000..4daa6de02d Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/terraform-v2-openapi/lambda-functions.zip differ diff --git a/tests/integration/testdata/start_api/terraform/terraform-v2-openapi/main.tf b/tests/integration/testdata/start_api/terraform/terraform-v2-openapi/main.tf new file mode 100644 index 0000000000..bdc78fd634 --- /dev/null +++ b/tests/integration/testdata/start_api/terraform/terraform-v2-openapi/main.tf @@ -0,0 +1,62 @@ +provider "aws" {} + +data "aws_region" "current" {} + +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + +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 = "app.lambda_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 = "api-body" + version = "1.0" + } + paths = { + "/hello" = { + get = { + x-amazon-apigateway-integration = { + httpMethod = "GET" + payloadFormatVersion = "2.0" + 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 = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] +} +EOF +} diff --git a/tests/integration/testdata/start_api/terraform/v1-lambda-authorizer/main.tf b/tests/integration/testdata/start_api/terraform/v1-lambda-authorizer/main.tf index b3dcc7b51c..c831423a86 100644 --- a/tests/integration/testdata/start_api/terraform/v1-lambda-authorizer/main.tf +++ b/tests/integration/testdata/start_api/terraform/v1-lambda-authorizer/main.tf @@ -1,5 +1,11 @@ provider "aws" {} +resource "random_uuid" "unique_id" { + keepers = { + my_key = "my_key" + } +} + resource "aws_api_gateway_authorizer" "header_authorizer" { name = "header_authorizer" rest_api_id = aws_api_gateway_rest_api.api.id @@ -18,18 +24,9 @@ resource "aws_api_gateway_authorizer" "request_authorizer" { type = "REQUEST" } -resource "aws_api_gateway_authorizer" "request_authorizer_empty" { - name = "request_authorizer" - 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 - identity_source = "" - type = "REQUEST" -} - resource "aws_lambda_function" "authorizer" { filename = "lambda-functions.zip" - function_name = "authorizer" + function_name = "authorizer-${random_uuid.unique_id.result}" role = aws_iam_role.invocation_role.arn handler = "handlers.auth_handler" runtime = "python3.8" @@ -38,7 +35,7 @@ resource "aws_lambda_function" "authorizer" { resource "aws_lambda_function" "hello_endpoint" { filename = "lambda-functions.zip" - function_name = "hello_lambda" + function_name = "hello_lambda-${random_uuid.unique_id.result}" role = aws_iam_role.invocation_role.arn handler = "handlers.hello_handler" runtime = "python3.8" @@ -61,14 +58,6 @@ resource "aws_api_gateway_method" "get_hello_request" { authorization = "CUSTOM" } -resource "aws_api_gateway_method" "get_hello_request_empty" { - rest_api_id = aws_api_gateway_rest_api.api.id - resource_id = aws_api_gateway_resource.hello_resource_request_empty.id - http_method = "GET" - authorizer_id = aws_api_gateway_authorizer.request_authorizer_empty.id - authorization = "CUSTOM" -} - resource "aws_api_gateway_resource" "hello_resource" { rest_api_id = aws_api_gateway_rest_api.api.id parent_id = aws_api_gateway_rest_api.api.root_resource_id @@ -81,16 +70,11 @@ resource "aws_api_gateway_resource" "hello_resource_request" { path_part = "hello-request" } -resource "aws_api_gateway_resource" "hello_resource_request_empty" { - rest_api_id = aws_api_gateway_rest_api.api.id - parent_id = aws_api_gateway_rest_api.api.root_resource_id - path_part = "hello-request-empty" -} - resource "aws_api_gateway_integration" "MyDemoIntegration" { rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.hello_resource.id http_method = aws_api_gateway_method.get_hello.http_method + integration_http_method = "POST" type = "AWS_PROXY" content_handling = "CONVERT_TO_TEXT" uri = aws_lambda_function.hello_endpoint.invoke_arn @@ -100,26 +84,18 @@ resource "aws_api_gateway_integration" "MyDemoIntegrationRequest" { rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.hello_resource_request.id http_method = aws_api_gateway_method.get_hello_request.http_method - type = "AWS_PROXY" - content_handling = "CONVERT_TO_TEXT" - uri = aws_lambda_function.hello_endpoint.invoke_arn -} - -resource "aws_api_gateway_integration" "MyDemoIntegrationRequestEmpty" { - rest_api_id = aws_api_gateway_rest_api.api.id - resource_id = aws_api_gateway_resource.hello_resource_request_empty.id - http_method = aws_api_gateway_method.get_hello_request_empty.http_method + integration_http_method = "POST" type = "AWS_PROXY" content_handling = "CONVERT_TO_TEXT" uri = aws_lambda_function.hello_endpoint.invoke_arn } resource "aws_api_gateway_rest_api" "api" { - name = "api" + name = "api-${random_uuid.unique_id.result}" } resource "aws_iam_role" "invocation_role" { - name = "iam_lambda" + name = "iam_lambda-${random_uuid.unique_id.result}" path = "/" assume_role_policy = <<EOF { diff --git a/tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/lambda-functions.zip b/tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/lambda-functions.zip new file mode 100644 index 0000000000..2845ce8129 Binary files /dev/null and b/tests/integration/testdata/start_api/terraform/v2-lambda-authorizer/lambda-functions.zip differ 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 = <<EOF +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] +} +EOF +} \ No newline at end of file diff --git a/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py b/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py index 5675d5e772..9882c2d28f 100644 --- a/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py +++ b/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py @@ -69,20 +69,12 @@ def test_invalid_coexist_options(self, iac_hook_wrapper_mock): ) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") def test_valid_hook_package_with_only_hook_id_option( - self, - iac_hook_wrapper_mock, - getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, - record_hook_telemetry_mock, + self, iac_hook_wrapper_mock, getcwd_mock, record_hook_telemetry_mock ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -111,20 +103,12 @@ def test_valid_hook_package_with_only_hook_id_option( self.assertEqual(opts.get("template_file"), self.metadata_path) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") def test_valid_hook_package_with_other_options( - self, - iac_hook_wrapper_mock, - getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, - record_hook_telemetry_mock, + self, iac_hook_wrapper_mock, getcwd_mock, record_hook_telemetry_mock ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -160,20 +144,40 @@ def test_valid_hook_package_with_other_options( record_hook_telemetry_mock.assert_called_once() @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") - def test_valid_hook_package_with_other_options_from_sam_config( + def test_skips_hook_package_with_help_option( self, iac_hook_wrapper_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True + getcwd_mock.return_value = self.cwd_path + + hook_name_option = HookNameOption( + param_decls=(self.name, self.opt), + force_prepare=True, + invalid_coexist_options=self.invalid_coexist_options, + ) + ctx = MagicMock() + ctx.default_map = {} + opts = { + "hook_name": self.terraform, + "help": True, + } + args = [] + hook_name_option.handle_parse_result(ctx, opts, args) + self.iac_hook_wrapper_instance_mock.prepare.assert_not_called() + record_hook_telemetry_mock.assert_not_called() + + @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") + @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") + @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") + def test_valid_hook_package_with_other_options_from_sam_config( + self, iac_hook_wrapper_mock, getcwd_mock, record_hook_telemetry_mock + ): + iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock getcwd_mock.return_value = self.cwd_path @@ -208,8 +212,6 @@ def test_valid_hook_package_with_other_options_from_sam_config( self.assertEqual(opts.get("template_file"), self.metadata_path) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") @@ -218,12 +220,9 @@ def test_valid_hook_package_with_skipping_prepare_hook_and_built_path_exists( iac_hook_wrapper_mock, path_exists_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -244,237 +243,6 @@ def test_valid_hook_package_with_skipping_prepare_hook_and_built_path_exists( self.assertEqual(opts.get("template_file"), None) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.GlobalConfig") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") - @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") - def test_valid_hook_package_with_disable_terraform_beta_feature( - self, - iac_hook_wrapper_mock, - path_exists_mock, - getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, - global_config_mock, - record_hook_telemetry_mock, - ): - iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = False - gc_mock = MagicMock() - global_config_mock.return_value = gc_mock - gc_mock.get_value.return_value = False - - getcwd_mock.return_value = self.cwd_path - - hook_name_option = HookNameOption( - param_decls=(self.name, self.opt), - force_prepare=True, - invalid_coexist_options=self.invalid_coexist_options, - ) - ctx = MagicMock() - ctx.default_map = {} - opts = { - "hook_name": self.terraform, - } - args = [] - hook_name_option.handle_parse_result(ctx, opts, args) - self.iac_hook_wrapper_instance_mock.prepare.assert_not_called() - self.assertEqual(opts.get("template_file"), None) - - @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") - @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") - def test_valid_hook_package_with_no_beta_feature_option( - self, - iac_hook_wrapper_mock, - path_exists_mock, - getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, - record_hook_telemetry_mock, - ): - iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = False - - getcwd_mock.return_value = self.cwd_path - - hook_name_option = HookNameOption( - param_decls=(self.name, self.opt), - force_prepare=True, - invalid_coexist_options=self.invalid_coexist_options, - ) - ctx = MagicMock() - opts = { - "hook_name": self.terraform, - "beta_features": False, - } - args = [] - hook_name_option.handle_parse_result(ctx, opts, args) - prompt_experimental_mock.assert_not_called() - self.iac_hook_wrapper_instance_mock.prepare.assert_not_called() - self.assertEqual(opts.get("template_file"), None) - - @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") - @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") - def test_valid_hook_package_with_beta_feature_option( - self, - iac_hook_wrapper_mock, - path_exists_mock, - getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, - record_hook_telemetry_mock, - ): - iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = False - - getcwd_mock.return_value = self.cwd_path - - hook_name_option = HookNameOption( - param_decls=(self.name, self.opt), - force_prepare=True, - invalid_coexist_options=self.invalid_coexist_options, - ) - ctx = MagicMock() - ctx.default_map = {} - opts = { - "hook_name": self.terraform, - "beta_features": True, - } - args = [] - hook_name_option.handle_parse_result(ctx, opts, args) - prompt_experimental_mock.assert_not_called() - self.iac_hook_wrapper_instance_mock.prepare.assert_called_once_with( - os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), - self.cwd_path, - False, - None, - None, - False, - None, - None, - ) - self.assertEqual(opts.get("template_file"), self.metadata_path) - - @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") - @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") - def test_valid_hook_package_with_beta_feature_option_in_sam_config( - self, - iac_hook_wrapper_mock, - path_exists_mock, - getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, - record_hook_telemetry_mock, - ): - metadata_path = "path/metadata.json" - cwd_path = "path/current" - invalid_coexist_options = ["t", "template", "template-file", "parameters-override"] - - iac_hook_wrapper_instance_mock = MagicMock() - iac_hook_wrapper_instance_mock.prepare.return_value = metadata_path - iac_hook_wrapper_mock.return_value = iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = False - - getcwd_mock.return_value = cwd_path - - hook_name_option = HookNameOption( - param_decls=(self.name, self.opt), - force_prepare=True, - invalid_coexist_options=invalid_coexist_options, - ) - ctx = MagicMock() - ctx.default_map = {"beta_features": True} - opts = { - "hook_name": self.terraform, - } - args = [] - hook_name_option.handle_parse_result(ctx, opts, args) - prompt_experimental_mock.assert_not_called() - iac_hook_wrapper_instance_mock.prepare.assert_called_once_with( - os.path.join(cwd_path, ".aws-sam-iacs", "iacs_metadata"), - cwd_path, - False, - None, - None, - False, - None, - None, - ) - self.assertEqual(opts.get("template_file"), metadata_path) - - @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.GlobalConfig") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") - @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") - @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") - def test_valid_hook_package_with_beta_feature_option_in_environment_variable( - self, - iac_hook_wrapper_mock, - path_exists_mock, - getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, - global_config_mock, - record_hook_telemetry_mock, - ): - metadata_path = "path/metadata.json" - cwd_path = "path/current" - invalid_coexist_options = ["t", "template", "template-file", "parameters-override"] - - iac_hook_wrapper_instance_mock = MagicMock() - iac_hook_wrapper_instance_mock.prepare.return_value = metadata_path - iac_hook_wrapper_mock.return_value = iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = False - - getcwd_mock.return_value = cwd_path - - hook_name_option = HookNameOption( - param_decls=(self.name, self.opt), - force_prepare=True, - invalid_coexist_options=invalid_coexist_options, - ) - ctx = MagicMock() - ctx.default_map = {} - opts = { - "hook_name": self.terraform, - } - gc_mock = MagicMock() - global_config_mock.return_value = gc_mock - gc_mock.get_value.return_value = True - args = [] - hook_name_option.handle_parse_result(ctx, opts, args) - prompt_experimental_mock.assert_not_called() - iac_hook_wrapper_instance_mock.prepare.assert_called_once_with( - os.path.join(cwd_path, ".aws-sam-iacs", "iacs_metadata"), - cwd_path, - False, - None, - None, - False, - None, - None, - ) - self.assertEqual(opts.get("template_file"), metadata_path) - - @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") @@ -483,12 +251,9 @@ def test_valid_hook_package_with_skipping_prepare_hook_and_built_path_does_not_e iac_hook_wrapper_mock, path_exists_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -519,8 +284,6 @@ def test_valid_hook_package_with_skipping_prepare_hook_and_built_path_does_not_e self.assertEqual(opts.get("template_file"), self.metadata_path) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") @@ -529,12 +292,9 @@ def test_valid_hook_package_with_use_container_and_build_image( iac_hook_wrapper_mock, path_exists_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -568,8 +328,6 @@ def test_valid_hook_package_with_use_container_and_build_image( self.assertEqual(opts.get("template_file"), self.metadata_path) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") @@ -578,12 +336,9 @@ def test_invalid_hook_package_with_use_container_and_no_build_image( iac_hook_wrapper_mock, path_exists_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -608,20 +363,15 @@ def test_invalid_hook_package_with_use_container_and_no_build_image( hook_name_option.handle_parse_result(ctx, opts, args) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") def test_invalid_parameter_hook_with_invalid_project_root_directory( self, iac_hook_wrapper_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -646,8 +396,6 @@ def test_invalid_parameter_hook_with_invalid_project_root_directory( hook_name_option.handle_parse_result(ctx, opts, args) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.isabs") @@ -658,12 +406,9 @@ def test_valid_parameter_hook_with_valid_absolute_project_root_directory( path_isabs_mock, path_exists_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -697,8 +442,6 @@ def test_valid_parameter_hook_with_valid_absolute_project_root_directory( self.assertEqual(opts.get("template_file"), self.metadata_path) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.isabs") @@ -709,12 +452,9 @@ def test_valid_parameter_hook_with_valid_relative_project_root_directory( path_isabs_mock, path_exists_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path @@ -748,8 +488,6 @@ def test_valid_parameter_hook_with_valid_relative_project_root_directory( self.assertEqual(opts.get("template_file"), self.metadata_path) @patch("samcli.commands._utils.custom_options.hook_name_option.record_hook_telemetry") - @patch("samcli.commands._utils.custom_options.hook_name_option.update_experimental_context") - @patch("samcli.commands._utils.custom_options.hook_name_option.prompt_experimental") @patch("samcli.commands._utils.custom_options.hook_name_option.os.getcwd") @patch("samcli.commands._utils.custom_options.hook_name_option.os.path.exists") @patch("samcli.commands._utils.custom_options.hook_name_option.IacHookWrapper") @@ -758,12 +496,9 @@ def test_valid_hook_package_with_use_container_false_and_no_build_image( iac_hook_wrapper_mock, path_exists_mock, getcwd_mock, - prompt_experimental_mock, - update_experimental_context_mock, record_hook_telemetry_mock, ): iac_hook_wrapper_mock.return_value = self.iac_hook_wrapper_instance_mock - prompt_experimental_mock.return_value = True getcwd_mock.return_value = self.cwd_path diff --git a/tests/unit/commands/buildcmd/test_command.py b/tests/unit/commands/buildcmd/test_command.py index 080ca7cbb4..b8c38bf661 100644 --- a/tests/unit/commands/buildcmd/test_command.py +++ b/tests/unit/commands/buildcmd/test_command.py @@ -68,40 +68,6 @@ def test_must_succeed_build(self, os_mock, BuildContextMock, mock_build_click): ctx_mock.run.assert_called_with() self.assertEqual(ctx_mock.run.call_count, 1) - @patch("samcli.commands.build.command.is_experimental_enabled") - @patch("samcli.commands.build.build_context.BuildContext") - def test_build_exits_supplied_hook_name(self, BuildContextMock, is_experimental_enabled_mock): - ctx_mock = Mock() - BuildContextMock.return_value.__enter__.return_value = ctx_mock - is_experimental_enabled_mock.return_value = False - - do_cli( - ctx_mock, - "function_identifier", - None, - "base_dir", - "build_dir", - "cache_dir", - "clean", - "use_container", - "cached", - "parallel", - "manifest_path", - "docker_network", - "skip_pull_image", - "parameter_overrides", - "mode", - (""), - "container_env_var_file", - (), - (), - hook_name="terraform", - build_in_source=None, - mount_with=MountMode.READ, - ) - self.assertEqual(ctx_mock.call_count, 0) - self.assertEqual(ctx_mock.run.call_count, 0) - class TestGetModeValueFromEnvvar(TestCase): def setUp(self): diff --git a/tests/unit/hook_packages/terraform/hooks/prepare/prepare_base.py b/tests/unit/hook_packages/terraform/hooks/prepare/prepare_base.py index cbb5ed1371..96193fa757 100644 --- a/tests/unit/hook_packages/terraform/hooks/prepare/prepare_base.py +++ b/tests/unit/hook_packages/terraform/hooks/prepare/prepare_base.py @@ -5,6 +5,7 @@ from samcli.hook_packages.terraform.hooks.prepare.translate import AWS_PROVIDER_NAME, NULL_RESOURCE_PROVIDER_NAME from samcli.lib.utils.resources import ( + AWS_APIGATEWAY_V2_AUTHORIZER, AWS_LAMBDA_FUNCTION as CFN_AWS_LAMBDA_FUNCTION, AWS_LAMBDA_LAYERVERSION, AWS_APIGATEWAY_RESOURCE, @@ -12,6 +13,10 @@ AWS_APIGATEWAY_STAGE, AWS_APIGATEWAY_METHOD, AWS_APIGATEWAY_AUTHORIZER, + AWS_APIGATEWAY_V2_API, + AWS_APIGATEWAY_V2_ROUTE, + AWS_APIGATEWAY_V2_STAGE, + AWS_APIGATEWAY_V2_INTEGRATION, ) from samcli.hook_packages.terraform.hooks.prepare.resources.internal import ( INTERNAL_API_GATEWAY_INTEGRATION, @@ -53,6 +58,13 @@ def setUp(self) -> 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") diff --git a/tests/unit/lib/sync/test_watch_manager.py b/tests/unit/lib/sync/test_watch_manager.py index 1e0c7922dd..61283d9ab9 100644 --- a/tests/unit/lib/sync/test_watch_manager.py +++ b/tests/unit/lib/sync/test_watch_manager.py @@ -353,6 +353,28 @@ def test_on_code_change_wrapper(self): self.executor.add_delayed_sync_flow.assert_any_call(flow1, dedup=True, wait_time=ANY) + def test_on_code_change_wrapper_opened_event_not_called(self): + flow1 = MagicMock() + resource_id_mock = MagicMock() + factory_mock = MagicMock() + event_mock = MagicMock() + event_mock.event_type = "opened" + + self.watch_manager._sync_flow_factory = factory_mock + factory_mock.create_sync_flow.return_value = flow1 + + self.watch_manager._on_code_change_wrapper(resource_id_mock)(event_mock) + + factory_mock.create_sync_flow.assert_not_called() + + def test_on_code_change_wrapper_missing_factory_sync_not_called(self): + resource_id_mock = MagicMock() + + self.watch_manager._sync_flow_factory = None + self.watch_manager._on_code_change_wrapper(resource_id_mock)() + + self.executor.add_delayed_sync_flow.assert_not_called() + def test_watch_sync_flow_exception_handler_missing_physical(self): sync_flow = MagicMock() sync_flow_exception = MagicMock(spec=SyncFlowException) diff --git a/tests/unit/lib/utils/test_file_observer.py b/tests/unit/lib/utils/test_file_observer.py index 9400aac775..7fb76b6100 100644 --- a/tests/unit/lib/utils/test_file_observer.py +++ b/tests/unit/lib/utils/test_file_observer.py @@ -384,8 +384,8 @@ def test_modification_event_got_fired_for_sub_path_and_check_sum_is_not_changed( @patch("samcli.lib.utils.file_observer.calculate_checksum") def test_modification_event_got_fired_for_path_got_deleted(self, calculate_checksum_mock, PathMock): event = Mock() - event.event_type == "deleted" - event.src_path = "parent_path1/path1/sub_path" + event.event_type = "deleted" + event.src_path = "parent_path1/path1" path_mock = Mock() PathMock.return_value = path_mock diff --git a/tests/unit/lib/utils/test_handler_observer.py b/tests/unit/lib/utils/test_handler_observer.py index 5d7041b6db..43071d75f8 100644 --- a/tests/unit/lib/utils/test_handler_observer.py +++ b/tests/unit/lib/utils/test_handler_observer.py @@ -103,7 +103,7 @@ def test_schedule_handler_not_static(self, wrapper_mock: MagicMock): schedule_mock = MagicMock() schedule_mock.return_value = watch - self.observer.schedule = schedule_mock + self.observer.schedule = schedule_mock # type: ignore result = self.observer.schedule_handler(bundle) @@ -131,7 +131,7 @@ def test_schedule_handler_static(self, wrapper_mock: MagicMock): schedule_mock = MagicMock() schedule_mock.side_effect = [watch, parent_watch] - self.observer.schedule = schedule_mock + self.observer.schedule = schedule_mock # type: ignore wrapper = MagicMock() wrapper_mock.return_value = wrapper