diff --git a/.gitignore b/.gitignore index a9dc95e..ac8a6e3 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,6 @@ source/cdk_solution_helper_py/helpers_cdk/build/* source/cdk_solution_helper_py/helpers_common/build/* source/scheduler/common/build/* source/scheduler/cdk/build/* + +# nightswatch test results +.nightswatch/functional/test-results.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index a2ca6d5..c7fe6e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.4.6] - 2025-06-26 + +### Fixed + +- Tests failure due to relative import issues + +### Changed + +- Upgraded requests to 2.32.4 +- Upgraded urllib3 to 2.5.0 + +## [1.4.5] - 2024-10-21 + +### Changed + +- Upgraded requests to 2.32.0 +- Upgraded urllib3 to 1.26.19 +- Upgraded black to 24.3.0 +- Onboarded anonymized operational metrics. + ## [1.4.4] - 2023-10-13 ### Changed diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3b64466..a0ea08c 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ ## Code of Conduct -This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 87f8a6e..30100b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,6 @@ documentation, we greatly value feedback and contributions from our community. Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. - ## Reporting Bugs/Feature Requests We welcome you to use the GitHub issue tracker to report bugs or suggest features. @@ -14,16 +13,16 @@ We welcome you to use the GitHub issue tracker to report bugs or suggest feature When filing an issue, please check [existing open](https://github.com/aws-solutions/maintaining-personalized-experiences-with-machine-learning/issues), or [recently closed](https://github.com/aws-solutions/maintaining-personalized-experiences-with-machine-learning/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: -* A reproducible test case or series of steps -* The version of our code being used -* Any modifications you've made relevant to the bug -* Anything unusual about your environment or deployment - +- A reproducible test case or series of steps +- The version of our code being used +- Any modifications you've made relevant to the bug +- Anything unusual about your environment or deployment ## Contributing via Pull Requests + Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: -1. You are working against the latest source on the *master* branch. +1. You are working against the latest source on the _master_ branch. 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. @@ -31,32 +30,30 @@ To send us a pull request, please: 1. Fork the repository. 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure all build processes execute successfully (see README.md for additional guidance). -4. Ensure all unit, integration, and/or snapshot tests pass, as applicable. -5. Commit to your fork using clear commit messages. -6. Send us a pull request, answering any default questions in the pull request interface. -7. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +3. Ensure local tests pass. +4. Commit to your fork using clear commit messages. +5. Send us a pull request, answering any default questions in the pull request interface. +6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). - ## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-solutions/maintaining-personalized-experiences-with-machine-learning/labels/help%20wanted) issues is a great place to start. +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-solutions/maintaining-personalized-experiences-with-machine-learning/labels/help%20wanted) issues is a great place to start. ## Code of Conduct + This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. - ## Security issue notifications -If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public GitHub issue. +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. ## Licensing See the [LICENSE](https://github.com/aws-solutions/maintaining-personalized-experiences-with-machine-learning/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. -We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. +We may ask you to sign a [Contributor License Agreement (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. \ No newline at end of file diff --git a/NOTICE.txt b/NOTICE.txt index 1865502..b7360f4 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,7 +1,7 @@ Maintaining Personalized Experiences with Machine Learning Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except +Licensed under the Apache-2.0 license (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at http://www.apache.org/licenses/ or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the @@ -12,38 +12,57 @@ THIRD PARTY COMPONENTS ********************** This software includes third party software subject to the following copyrights: -Apache Avro under the Apache License 2.0 -AWS Lambda Java Support Libraries under the Apache License Version 2.0 -AWS Lambda Powertools for Python under the MIT No Attribution license -AWS SDK under the Apache License Version 2.0 -boto3 under the Apache License Version 2.0 -black under the Massachusetts Institute of Technology (MIT) license -click under the BSD 3-Clause license -coverage under the Apache License Version 2.0 -crhelper under the Apache License Version 2.0 -cronex under the Massachusetts Institute of Technology (MIT) license -docker-py under the Apache License Version 2.0 -Gradle under the Apache License Version 2.0 -jmespath under the Apache License Version 2.0 +avro under the Apache-2.0 license +AWS Lambda Java Support Libraries under the Apache-2.0 license +aws-lambda-powertools under the MIT No Attribution license +aws_cdk_lib under the Apache-2.0 license under the Apache-2.0 license +aws_solutions_constructs.aws_lambda_sns under the Apache-2.0 license +aws-cdk.aws-servicecatalogappregistry-alpha under the Apache-2.0 license +cdk-nag under the Apache-2.0 license +AWS SDK under the Apache-2.0 license +boto3 under the Apache-2.0 license +black under the MIT license +Click under the BSD 3-Clause license +coverage under the Apache-2.0 license +crhelper under the Apache-2.0 license +cronex under the MIT license +docker-py under the Apache-2.0 license +docker under the Apache-2.0 license +responses under the Apache-2.0 license +Gradle under the Apache-2.0 license +jmespath under the Apache-2.0 license junit under the Eclipse Public License Version 2.0 -moto under the Apache License Version 2.0 -pytest under the Massachusetts Institute of Technology (MIT) license -pytest-cov under the Massachusetts Institute of Technology (MIT) license -pytest-mock under the Massachusetts Institute of Technology (MIT) license -pytest-env under the Massachusetts Institute of Technology (MIT) license -PyYAML under the Massachusetts Institute of Technology (MIT) license -requests under the Apache License Version 2.0 -requests-mock under the Apache License Version 2.0 -rich under the Massachusetts Institute of Technology (MIT) license -tenacity under the Apache License Version 2.0 -quartz-scheduler under the Apache License Version 2.0 -parsedatetime under the Apache License Version 2.0 -urllib3 under the Massachusetts Institute of Technology (MIT) license -setuptools under the Massachusetts Institute of Technology (MIT) license -pipenv under the Massachusetts Institute of Technology (MIT) license -virtualenv under the Massachusetts Institute of Technology (MIT) license -tox under the Massachusetts Institute of Technology (MIT) license -tox-pyenv under the Apache License Version 2.0 -poetry under the Massachusetts Institute of Technology (MIT) license +org.jacoco/org.jacoco.core under the Eclipse Public License 2.0 license(s) +./package under the 0BSD license +moto under the Apache-2.0 license +pytest under the MIT license +pytest-cov under the MIT license +pytest-mock under the MIT license +pytest-env under the MIT license +pyyaml under the MIT license +requests under the Apache-2.0 license +requests-mock under the Apache-2.0 license +rich under the MIT license +tenacity under the Apache-2.0 license +quartz-scheduler under the Apache-2.0 license +parsedatetime under the Apache-2.0 license +urllib3 under the MIT license +setuptools under the MIT license +pipenv under the MIT license +virtualenv under the MIT license +tox under the MIT license +tox-pyenv under the Apache-2.0 license +poetry under the MIT license +aws-xray-sdk under the Apache-2.0 license -The Apache License Version Version 2.0 is included in LICENSE.txt. \ No newline at end of file + +The Apache License Version Version 2.0 is included in LICENSE.txt. + +******************** +OPEN SOURCE LICENSES +******************** + +0BSD - https://opensource.org/licenses/0BSD +Apache-2.0 - https://opensource.org/licenses/Apache-2.0 +MIT - https://opensource.org/licenses/MIT +MIT-0 - https://opensource.org/licenses/MIT-0 diff --git a/README.md b/README.md index 8869564..e715bc9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Maintaining Personalized Experiences with Machine Learning +⚠️🚨⚠️ **Important: [The Maintaining Personalized Experiences with Machine Learning](https://aws.amazon.com/solutions/implementations/maintaining-personalized-experiences-with-ml/) solution will retire in October 2025. Deployments (via CloudFormation or GitHub) will remain operational, but customers will assume responsibility for maintenance and API-related updates after support ends. Customers can explore using [Amazon Personalize](https://aws.amazon.com/personalize/) to deliver hyper-personalized user experiences in real-time at scale to improve user engagement, customer loyalty, and business results.** ⚠️🚨⚠️ + The Maintaining Personalized Experiences with Machine Learning solution provides a mechanism to automate much of the workflow around Amazon Personalize. This includes dataset group creation, dataset creation and import, solution creation, solution version creation, campaign creation and batch inference job creation @@ -607,7 +609,7 @@ To customize the solution, follow the steps below: The following procedures assumes that all the OS-level configuration has been completed. They are: - [AWS Command Line Interface](https://aws.amazon.com/cli/) -- [Python](https://www.python.org/) 3.9 or newer +- [Python](https://www.python.org/) 3.11 or newer - [Node.js](https://nodejs.org/en/) 16.x or newer - [AWS CDK](https://aws.amazon.com/cdk/) 2.88.0 or newer - [Amazon Corretto OpenJDK](https://docs.aws.amazon.com/corretto/) 17.0.4.1 @@ -706,8 +708,7 @@ After running the command, you can deploy the template: ## Collection of operational metrics -This solution collects anonymous operational metrics to help AWS improve the quality of features of the solution. -For more information, including how to disable this capability, please see the [implementation guide](https://docs.aws.amazon.com/solutions/latest/maintaining-personalized-experiences-with-ml/reference.html). + This solution collects anonymized operational metrics to help AWS improve the quality and features of the solution. For more information, including how to disable this capability, please see the [implementation guide](https://docs.aws.amazon.com/solutions/latest/maintaining-personalized-experiences-with-ml/reference.html). --- diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..004e849 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +## Reporting Security Issues + +We take all security reports seriously. When we receive such reports, +we will investigate and subsequently address any potential vulnerabilities as +quickly as possible. If you discover a potential security issue in this project, +please notify AWS/Amazon Security via our [vulnerability reporting page] +(http://aws.amazon.com/security/vulnerability-reporting/) or directly via email +to [AWS Security](mailto:aws-security@amazon.com). +Please do *not* create a public GitHub issue in this project. \ No newline at end of file diff --git a/deployment/run-unit-tests.sh b/deployment/run-unit-tests.sh index 6238d7b..303b5cc 100644 --- a/deployment/run-unit-tests.sh +++ b/deployment/run-unit-tests.sh @@ -41,7 +41,9 @@ echo "-------------------------------------------------------------------------- echo "[Env] Create virtual environment and install dependencies" echo "------------------------------------------------------------------------------" -virtualenv .venv +#Set the python version to 3.11 +pip install virtualenv +python3.11 -m virtualenv .venv source .venv/bin/activate cd $source_dir diff --git a/source/cdk_solution_helper_py/README.md b/source/cdk_solution_helper_py/README.md index 2bdd645..c515abb 100644 --- a/source/cdk_solution_helper_py/README.md +++ b/source/cdk_solution_helper_py/README.md @@ -11,7 +11,7 @@ This README summarizes using the tool. Install this package. It requires at least -- Python 3.9 +- Python 3.11 - AWS CDK version 2.75.0 or higher To install the packages: diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_hash/hash.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_hash/hash.py index a7e7418..87c3bf6 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_hash/hash.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_hash/hash.py @@ -20,6 +20,7 @@ from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions from cdk_nag import NagSuppressions from cdk_nag import NagPackSuppression @@ -58,6 +59,10 @@ def __init__( ), ], ) + add_cfn_guard_suppressions( + self._resource_name_function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) NagSuppressions.add_resource_suppressions(self._resource_name_function.role, [ NagPackSuppression( diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_name/name.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_name/name.py index d540f79..eca0cab 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_name/name.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/resource_name/name.py @@ -22,6 +22,7 @@ from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions from cdk_nag import NagSuppressions from cdk_nag import NagPackSuppression @@ -71,6 +72,11 @@ def __init__( 'which do not have a resource arn')], apply_to_children=True) + add_cfn_guard_suppressions( + self._resource_name_function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + properties = { "ServiceToken": self._resource_name_function.function_arn, "Purpose": purpose, @@ -88,6 +94,11 @@ def __init__( properties=properties, ) + add_cfn_guard_suppressions( + self._resource_name_function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + @property def resource_name(self): return self.resource_name_resource.get_att("Name") diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/metrics.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/metrics.py index 6e2176b..2de6089 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/metrics.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/metrics.py @@ -23,7 +23,7 @@ from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class Metrics(Construct): """Used to track anonymous solution deployment metrics.""" @@ -65,8 +65,8 @@ def __init__( properties = { "ServiceToken": self._metrics_function.function_arn, - "Solution": self.node.try_get_context("SOLUTION_NAME"), - "Version": self.node.try_get_context("VERSION"), + "Solution": self.node.try_get_context("SOLUTION_ID"), + "Version": self.node.try_get_context("SOLUTION_VERSION"), "Region": Aws.REGION, **metrics, } @@ -78,3 +78,8 @@ def __init__( ) self.solution_metrics.override_logical_id("SolutionMetricsAnonymousData") self.solution_metrics.cfn_options.condition = self._send_anonymous_usage_data + + add_cfn_guard_suppressions( + self._metrics_function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) \ No newline at end of file diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/metrics.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/metrics.py index a2d0f50..9d046f4 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/metrics.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/metrics.py @@ -14,7 +14,7 @@ import logging import uuid -from datetime import datetime +from datetime import datetime, timezone from os import getenv import requests @@ -54,11 +54,11 @@ def send_metrics(event, _): headers = {"Content-Type": "application/json"} payload = { "Solution": resource_properties["Solution"], + "Version": resource_properties["Version"], "UUID": random_id, - "TimeStamp": datetime.utcnow().isoformat(), + "TimeStamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f"), "Data": _sanitize_data(event), } - logger.info(f"Sending payload: {payload}") response = requests.post(METRICS_ENDPOINT, json=payload, headers=headers, timeout=REQUESTS_TIMEOUT) logger.info(f"Response from metrics endpoint: {response.status_code} {response.reason}") diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/requirements.txt b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/requirements.txt index 0e04dcc..95f629e 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/requirements.txt +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/cfn_custom_resources/solutions_metrics/src/custom_resources/requirements.txt @@ -1,3 +1,3 @@ -requests==2.31.0 -urllib3==1.26.17 +requests==2.32.4 +urllib3==2.5.0 crhelper==2.0.11 diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/bundling.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/bundling.py index bff7081..d5d1786 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/bundling.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/bundling.py @@ -25,7 +25,7 @@ from aws_solutions.cdk.helpers import copytree -DEFAULT_RUNTIME = Runtime.PYTHON_3_9 +DEFAULT_RUNTIME = Runtime.PYTHON_3_11 BUNDLER_DEPENDENCIES_CACHE = "/var/dependencies" REQUIREMENTS_TXT_FILE = "requirements.txt" REQUIREMENTS_PIPENV_FILE = "Pipfile" diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/function.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/function.py index 7c66898..acad14e 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/function.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/python/function.py @@ -27,7 +27,7 @@ from aws_solutions.cdk.aws_lambda.python.bundling import SolutionsPythonBundling from aws_solutions.cdk.aws_lambda.python.hash_utils import DirectoryHash -DEFAULT_RUNTIME = Runtime.PYTHON_3_9 +DEFAULT_RUNTIME = Runtime.PYTHON_3_11 DEPENDENCY_EXCLUDES = ["*.pyc"] @@ -62,7 +62,7 @@ def __init__( if not kwargs.get("role"): kwargs["role"] = self._create_role() - # python 3.9 is selected to support custom resources and inline code + # python 3.11 is selected to support custom resources and inline code if not kwargs.get("runtime"): kwargs["runtime"] = DEFAULT_RUNTIME diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/cfn_guard.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/cfn_guard.py new file mode 100644 index 0000000..78b6fcb --- /dev/null +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/cfn_guard.py @@ -0,0 +1,46 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from dataclasses import dataclass +from typing import List + +import jsii +from aws_cdk import CfnResource, IAspect +from constructs import IConstruct + + +def add_cfn_guard_suppressions( + resource: CfnResource, suppressions: List[str] +): + if resource.node.default_child: + resource.node.default_child.add_metadata( + "guard", + { + "SuppressedRules": suppressions + }, + ) + else: + resource.add_metadata( + "guard", + { + "SuppressedRules": suppressions + }, + ) + +@jsii.implements(IAspect) +class CfnGuardSuppressResourceList: + """Suppress certain cfn_guard warnings that can be ignored by this solution""" + + def __init__(self, resource_suppressions: dict): + self.resource_suppressions = resource_suppressions + + def visit(self, node: IConstruct): + if "is_cfn_element" in dir(node) and \ + node.is_cfn_element(node) and \ + getattr(node, "cfn_resource_type", None) is not None and \ + node.cfn_resource_type in self.resource_suppressions: + add_cfn_guard_suppressions(node, self.resource_suppressions[node.cfn_resource_type]) + elif "is_cfn_element" in dir(node.node.default_child) and \ + getattr(node.node.default_child, "cfn_resource_type", None) is not None and \ + node.node.default_child.cfn_resource_type in self.resource_suppressions: + add_cfn_guard_suppressions(node.node.default_child, self.resource_suppressions[node.node.default_child.cfn_resource_type]) \ No newline at end of file diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/scripts/build_s3_cdk_dist.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/scripts/build_s3_cdk_dist.py index 3ed63ce..0f016a5 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/scripts/build_s3_cdk_dist.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/scripts/build_s3_cdk_dist.py @@ -228,6 +228,7 @@ def source_code_package(ctx, ignore, solution_name): "CONTRIBUTING.md", "CHANGELOG.md", ".gitignore", + "SECURITY.md" ] # read the gitignore diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solutionstep.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solutionstep.py index 415bb8b..244dc2c 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solutionstep.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solutionstep.py @@ -127,7 +127,7 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs): kwargs["layers"] = kwargs.get("layers", []) kwargs["tracing"] = Tracing.ACTIVE kwargs["timeout"] = Duration.seconds(15) - kwargs["runtime"] = Runtime("python3.9", RuntimeFamily.PYTHON) + kwargs["runtime"] = Runtime("python3.11", RuntimeFamily.PYTHON) super().__init__( scope, diff --git a/source/cdk_solution_helper_py/helpers_cdk/setup.py b/source/cdk_solution_helper_py/helpers_cdk/setup.py index f59c70b..4af645b 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/setup.py +++ b/source/cdk_solution_helper_py/helpers_cdk/setup.py @@ -53,14 +53,14 @@ def get_version(): "aws_cdk_lib==2.88.0", "Click==8.1.3", "boto3==1.26.47", - "requests==2.31.0", + "requests==2.32.4", "crhelper==2.0.11", ], entry_points=""" [console_scripts] build-s3-cdk-dist=aws_solutions.cdk.scripts.build_s3_cdk_dist:cli """, - python_requires=">=3.9", + python_requires=">=3.11", classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -70,6 +70,8 @@ def get_version(): "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Code Generators", "Topic :: Utilities", "Typing :: Typed", diff --git a/source/cdk_solution_helper_py/helpers_common/setup.py b/source/cdk_solution_helper_py/helpers_common/setup.py index a89ffad..f28cfb4 100644 --- a/source/cdk_solution_helper_py/helpers_common/setup.py +++ b/source/cdk_solution_helper_py/helpers_common/setup.py @@ -45,7 +45,7 @@ def get_version(): "boto3==1.26.47", "pip>=22.3.1", ], - python_requires=">=3.9", + python_requires=">=3.11", classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -55,6 +55,8 @@ def get_version(): "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Code Generators", "Topic :: Utilities", "Typing :: Typed", diff --git a/source/cdk_solution_helper_py/requirements-dev.txt b/source/cdk_solution_helper_py/requirements-dev.txt index 5637c22..8a304e7 100644 --- a/source/cdk_solution_helper_py/requirements-dev.txt +++ b/source/cdk_solution_helper_py/requirements-dev.txt @@ -2,16 +2,16 @@ aws_cdk_lib==2.88.0 aws-cdk.aws-servicecatalogappregistry-alpha==2.88.0a0 black boto3==1.26.47 -requests==2.31.0 +requests==2.32.4 crhelper==2.0.11 -Click~=8.1.3 -moto~=2.3.0 +Click==8.1.3 +moto==2.3.0 pipenv -poetry~=1.6.1 -pytest>=7.4.2 -pytest-cov>=4.0.0 -pytest-mock>=3.10.0 -tox~=2.9.1 +poetry==1.6.1 +pytest==7.4.4 +pytest-cov==4.1.0 +pytest-mock==3.12.0 +tox==4.11.4 tox-pyenv -e ./source/cdk_solution_helper_py/helpers_cdk -e ./source/cdk_solution_helper_py/helpers_common diff --git a/source/infrastructure/cdk.json b/source/infrastructure/cdk.json index 14df4f1..1e568fd 100644 --- a/source/infrastructure/cdk.json +++ b/source/infrastructure/cdk.json @@ -3,7 +3,7 @@ "context": { "SOLUTION_NAME": "Maintaining Personalized Experiences with Machine Learning", "SOLUTION_ID": "SO0170", - "SOLUTION_VERSION": "v1.4.4", + "SOLUTION_VERSION": "v1.4.5", "APP_REGISTRY_NAME": "personalized-experiences-ML", "APPLICATION_TYPE": "AWS-Solutions", "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true diff --git a/source/infrastructure/deploy.py b/source/infrastructure/deploy.py index 3587f85..5f9e98e 100644 --- a/source/infrastructure/deploy.py +++ b/source/infrastructure/deploy.py @@ -37,7 +37,7 @@ def build_app(context): stack = PersonalizeStack( app, "PersonalizeStack", - description=f"Deploy, deliver and maintain personalized experiences with Amazon Personalize", + description=f"Maintaining Personalized Experiences with Machine Learning", template_filename="maintaining-personalized-experiences-with-machine-learning.template", synthesizer=solution.synthesizer, ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py b/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py index eebc0a1..f2ddcac 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py @@ -21,6 +21,7 @@ from cdk_nag import NagPackSuppression from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateBatchInferenceJob(SolutionStep): @@ -74,6 +75,11 @@ def __init__( 'which do not have a resource arn')], apply_to_children=True) + add_cfn_guard_suppressions( + self.personalize_batch_inference_rw_role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + super().__init__( scope, id, @@ -84,6 +90,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): # personalize resource permissions self.function.add_to_role_policy( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_batch_segment_job.py b/source/infrastructure/personalize/aws_lambda/functions/create_batch_segment_job.py index 6e83fee..39f1f80 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_batch_segment_job.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_batch_segment_job.py @@ -21,7 +21,7 @@ from cdk_nag import NagPackSuppression from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateBatchSegmentJob(SolutionStep): def __init__( @@ -74,6 +74,11 @@ def __init__( 'which do not have a resource arn')], apply_to_children=True) + add_cfn_guard_suppressions( + self.personalize_batch_inference_rw_role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + super().__init__( scope, id, @@ -84,6 +89,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): # personalize resource permissions self.function.add_to_role_policy( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py b/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py index 7542aeb..89c1fe0 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py @@ -17,7 +17,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateCampaign(SolutionStep): def __init__( @@ -34,6 +34,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_config.py b/source/infrastructure/personalize/aws_lambda/functions/create_config.py index 298f4ae..be13a19 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_config.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_config.py @@ -21,6 +21,7 @@ from aws_solutions.cdk.aws_lambda.environment import Environment from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateConfig(SolutionsPythonFunction): @@ -30,7 +31,7 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs): kwargs["libraries"] = [Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"] kwargs["tracing"] = Tracing.ACTIVE kwargs["timeout"] = Duration.seconds(90) - kwargs["runtime"] = Runtime("python3.9", RuntimeFamily.PYTHON) + kwargs["runtime"] = Runtime("python3.11", RuntimeFamily.PYTHON) super().__init__(scope, construct_id, entrypoint, function_name, **kwargs) @@ -41,6 +42,11 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs): [CfnNagSuppression("W12", "IAM policy for AWS X-Ray requires an allow on *")], ) + add_cfn_guard_suppressions( + self.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + self._set_permissions() def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py b/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py index a45b9d5..17b3b24 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py @@ -19,7 +19,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateDataset(SolutionStep): def __init__( @@ -38,6 +38,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py index 117349f..38fc1a0 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py @@ -21,6 +21,7 @@ from aws_solutions.cdk.aws_lambda.cfn_custom_resources.resource_hash import ResourceHash from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateDatasetGroup(SolutionStep): @@ -47,6 +48,11 @@ def __init__( **kwargs, ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): """ Set up a role to allow Amazon Personalize to access a KMS key in this account. @@ -161,3 +167,8 @@ def _set_permissions(self): "KMS_KEY_ARN", Fn.condition_if(self.kms_enabled.node.id, self.kms_key.value_as_string, "").to_string(), ) + + add_cfn_guard_suppressions( + kms_role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py index 55b1e9f..72255e7 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py @@ -23,6 +23,7 @@ from cdk_nag import NagPackSuppression from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateDatasetImportJob(SolutionStep): @@ -83,6 +84,11 @@ def __init__( 'which do not have a resource arn')], apply_to_children=True) + add_cfn_guard_suppressions( + self.personalize_role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + super().__init__( scope, id, @@ -94,6 +100,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): # personalize resource permissions self.function.add_to_role_policy( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py b/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py index 5e87aa3..ac40bf4 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py @@ -19,7 +19,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateEventTracker(SolutionStep): def __init__( @@ -51,6 +51,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_filter.py b/source/infrastructure/personalize/aws_lambda/functions/create_filter.py index cc436a9..313d9d9 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_filter.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_filter.py @@ -19,7 +19,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateFilter(SolutionStep): def __init__( @@ -38,6 +38,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_recommender.py b/source/infrastructure/personalize/aws_lambda/functions/create_recommender.py index 9449502..b5796ba 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_recommender.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_recommender.py @@ -17,6 +17,8 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions + class CreateRecommender(SolutionStep): @@ -34,6 +36,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( @@ -49,6 +56,7 @@ def _set_permissions(self): resources=[ f"arn:{Aws.PARTITION}:personalize:{Aws.REGION}:{Aws.ACCOUNT_ID}:recommender/*", f"arn:{Aws.PARTITION}:personalize:{Aws.REGION}:{Aws.ACCOUNT_ID}:dataset-group/*", + f"arn:{Aws.PARTITION}:personalize:::recipe/*" ], ) ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py b/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py index 29313d1..2a6c8f3 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py @@ -15,7 +15,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateScheduledTask(SolutionStep): def __init__( @@ -32,5 +32,10 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): pass # NOSONAR (python:S1186) - no permissions required diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_schema.py b/source/infrastructure/personalize/aws_lambda/functions/create_schema.py index c280421..5bd7286 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_schema.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_schema.py @@ -19,7 +19,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateSchema(SolutionStep): def __init__( @@ -38,6 +38,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_solution.py b/source/infrastructure/personalize/aws_lambda/functions/create_solution.py index 7b686c9..065ccae 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_solution.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_solution.py @@ -17,7 +17,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateSolution(SolutionStep): def __init__( @@ -34,6 +34,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( @@ -49,6 +54,7 @@ def _set_permissions(self): resources=[ f"arn:{Aws.PARTITION}:personalize:{Aws.REGION}:{Aws.ACCOUNT_ID}:solution/*", f"arn:{Aws.PARTITION}:personalize:{Aws.REGION}:{Aws.ACCOUNT_ID}:dataset-group/*", + f"arn:{Aws.PARTITION}:personalize:::recipe/*" ], ) ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py b/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py index 38e2a8b..ce0c57e 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py @@ -17,6 +17,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateSolutionVersion(SolutionStep): @@ -34,6 +35,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_to_role_policy( statement=iam.PolicyStatement( diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py b/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py index 2ae65b0..1649096 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py @@ -15,6 +15,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateTimestamp(SolutionStep): @@ -31,5 +32,10 @@ def __init__( entrypoint=(Path(__file__).absolute().parents[4] / "aws_lambda" / "create_timestamp" / "handler.py"), ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): pass # NOSONAR (python:S1186) - no permissions required diff --git a/source/infrastructure/personalize/aws_lambda/functions/prepare_input.py b/source/infrastructure/personalize/aws_lambda/functions/prepare_input.py index d5b76d5..95d3b5f 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/prepare_input.py +++ b/source/infrastructure/personalize/aws_lambda/functions/prepare_input.py @@ -16,7 +16,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class PrepareInput(SolutionStep): def __init__( @@ -33,5 +33,10 @@ def __init__( libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): pass # NOSONAR (python:S1186) - no permissions required diff --git a/source/infrastructure/personalize/aws_lambda/functions/s3_event.py b/source/infrastructure/personalize/aws_lambda/functions/s3_event.py index 5b4d6bd..b2b99e5 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/s3_event.py +++ b/source/infrastructure/personalize/aws_lambda/functions/s3_event.py @@ -23,7 +23,7 @@ from aws_solutions.cdk.aws_lambda.environment import Environment from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class S3EventHandler(SolutionsPythonFunction): def __init__( @@ -34,7 +34,7 @@ def __init__( kwargs["libraries"] = [Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"] kwargs["tracing"] = Tracing.ACTIVE kwargs["timeout"] = Duration.seconds(15) - kwargs["runtime"] = Runtime("python3.9", RuntimeFamily.PYTHON) + kwargs["runtime"] = Runtime("python3.11", RuntimeFamily.PYTHON) super().__init__(scope, construct_id, entrypoint, function_name, **kwargs) @@ -46,6 +46,11 @@ def __init__( [CfnNagSuppression("W12", "IAM policy for AWS X-Ray requires an allow on *")], ) + add_cfn_guard_suppressions( + self.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + bucket.grant_read(self, objects_key_pattern="train/*") state_machine.grant_start_execution(self) diff --git a/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt b/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt index bce36e9..83a0432 100644 --- a/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt +++ b/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt @@ -1,6 +1,6 @@ ../../../../../../cdk_solution_helper_py/helpers_common ../../../../../../scheduler/common -avro~=1.11.3 +avro==1.11.3 cronex==0.1.3.1 jmespath==1.0.1 parsedatetime==2.6 diff --git a/source/infrastructure/personalize/sns/notifications.py b/source/infrastructure/personalize/sns/notifications.py index 4d3e700..d65520f 100644 --- a/source/infrastructure/personalize/sns/notifications.py +++ b/source/infrastructure/personalize/sns/notifications.py @@ -26,7 +26,7 @@ from aws_solutions.cdk.aspects import ConditionalResources from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep - +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class Notifications(SolutionStep): def __init__( @@ -52,6 +52,11 @@ def __init__( libraries=[Path(__file__).absolute().parents[3] / "aws_lambda" / "shared"], ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def create_sns(self): """ Create the SNS topic using AWS Solutions Constructs diff --git a/source/infrastructure/personalize/stack.py b/source/infrastructure/personalize/stack.py index d2f3abd..9def2ac 100644 --- a/source/infrastructure/personalize/stack.py +++ b/source/infrastructure/personalize/stack.py @@ -398,6 +398,10 @@ def __init__(self, scope: Construct, construct_id: str, *args, **kwargs) -> None NotificationKeyFilter(prefix="train/", suffix=".json"), ) + # Manually add a dependency between the data bucket's notification and policy settings + # to prevent a race condition between the settings during resource tear down. + data_bucket.node.find_child("Notifications").node.add_dependency(data_bucket.node.find_child("Policy")) + # Handle suppressions for the notification handler resource generated by CDK bucket_notification_handler = self.node.try_find_child( "BucketNotificationsHandler050a0587b7544547bf325f094a3db834" diff --git a/source/infrastructure/setup.py b/source/infrastructure/setup.py index 30ddfa9..f88655f 100644 --- a/source/infrastructure/setup.py +++ b/source/infrastructure/setup.py @@ -38,7 +38,7 @@ "aws-cdk-lib==2.88.0", "pip>=22.3.1", ], - python_requires=">=3.9", + python_requires=">=3.11", classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -48,6 +48,8 @@ "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Code Generators", "Topic :: Utilities", "Typing :: Typed", diff --git a/source/requirements-dev.txt b/source/requirements-dev.txt index 194be3b..d8b1c17 100644 --- a/source/requirements-dev.txt +++ b/source/requirements-dev.txt @@ -1,21 +1,21 @@ -avro~=1.11.3 -black +avro==1.11.3 +black==24.3.0 boto3==1.26.47 aws_cdk_lib==2.88.0 aws_solutions_constructs.aws_lambda_sns==2.41.0 aws-cdk.aws-servicecatalogappregistry-alpha==2.88.0a0 cdk-nag==2.27.107 -requests==2.31.0 +requests==2.32.4 crhelper==2.0.11 cronex==0.1.3.1 moto==2.3.0 parsedatetime==2.6 -pytest>=7.4.2 -pytest-cov>=4.0.0 -pytest-env>=0.8.1 -pytest-mock>=3.10.0 -pyyaml==6.0 -responses~=0.17.0 +pytest==7.4.4 +pytest-cov==4.1.0 +pytest-env==1.1.3 +pytest-mock==3.12.0 +pyyaml==6.0.2 +responses==0.17.0 tenacity==8.0.1 -e cdk_solution_helper_py/helpers_cdk -e cdk_solution_helper_py/helpers_common diff --git a/source/scheduler/README.md b/source/scheduler/README.md index f3929fb..915c40a 100644 --- a/source/scheduler/README.md +++ b/source/scheduler/README.md @@ -10,8 +10,8 @@ This README summarizes using the scheduler. Install this package. It requires at least: -- Python 3.9 -- AWS CDK version 2.75.0 or higher +- Python 3.11 +- AWS CDK version 2.88.0 or higher To install the packages: diff --git a/source/scheduler/cdk/__init__.py b/source/scheduler/cdk/__init__.py new file mode 100644 index 0000000..ef2f9eb --- /dev/null +++ b/source/scheduler/cdk/__init__.py @@ -0,0 +1,12 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/create_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/create_scheduled_task.py index 7385af8..edc730f 100644 --- a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/create_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/create_scheduled_task.py @@ -20,6 +20,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class CreateScheduledTask(SolutionStep): @@ -46,6 +47,11 @@ def __init__( entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_environment("DDB_SCHEDULER_STEPFUNCTION", self.state_machine_arn) self.function.add_to_role_policy( diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/delete_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/delete_scheduled_task.py index 9cbad03..89fd66a 100644 --- a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/delete_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/delete_scheduled_task.py @@ -20,6 +20,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class DeleteScheduledTask(SolutionStep): @@ -44,6 +45,11 @@ def __init__( entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_environment("DDB_SCHEDULER_STEPFUNCTION", self.state_machine_arn) self.function.add_to_role_policy( diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/read_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/read_scheduled_task.py index ffb9887..832ce34 100644 --- a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/read_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/read_scheduled_task.py @@ -20,6 +20,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class ReadScheduledTask(SolutionStep): @@ -44,6 +45,11 @@ def __init__( entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_environment("DDB_SCHEDULER_STEPFUNCTION", self.state_machine_arn) self.function.add_to_role_policy( diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt index a9f64cd..c05fab5 100644 --- a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt @@ -1,4 +1,4 @@ -avro~=1.11.3 +avro==1.11.3 cronex==0.1.3.1 jmespath==1.0.1 parsedatetime==2.6 diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/update_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/update_scheduled_task.py index 3e667eb..40dd44a 100644 --- a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/update_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/update_scheduled_task.py @@ -20,6 +20,7 @@ from constructs import Construct from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions class UpdateScheduledTask(SolutionStep): @@ -46,6 +47,11 @@ def __init__( entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", ) + add_cfn_guard_suppressions( + self.function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) + def _set_permissions(self): self.function.add_environment("DDB_SCHEDULER_STEPFUNCTION", self.state_machine_arn) self.function.add_to_role_policy( diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/construct.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/construct.py index 9be461a..86f648c 100644 --- a/source/scheduler/cdk/aws_solutions/scheduler/cdk/construct.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/construct.py @@ -40,6 +40,7 @@ from aws_solutions.cdk.aws_lambda.java.function import SolutionsJavaFunction from aws_solutions.cdk.aws_lambda.layers.aws_lambda_powertools import PowertoolsLayer from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression +from aws_solutions.cdk.cfn_guard import add_cfn_guard_suppressions from aws_solutions.scheduler.cdk.aws_lambda import ( CreateScheduledTask, ReadScheduledTask, @@ -364,6 +365,8 @@ def _scheduler_table(self, scope: Construct) -> ddb.Table: ) tasks_table.node.default_child.override_logical_id("PersonalizeScheduledTasks") + add_cfn_guard_suppressions(tasks_table,["DYNAMODB_TABLE_ENCRYPTED_KMS"]) + return tasks_table def _scheduler_function(self, scope: Construct, construct_id: str) -> SolutionsJavaFunction: @@ -391,5 +394,10 @@ def _scheduler_function(self, scope: Construct, construct_id: str) -> SolutionsJ solutions_java_function.role.node.try_find_child("DefaultPolicy").node.find_child("Resource"), [CfnNagSuppression("W12", "IAM policy for AWS X-Ray requires an allow on *")], ) + + add_cfn_guard_suppressions( + solutions_java_function.role.node.try_find_child("Resource"), + ["IAM_NO_INLINE_POLICY_CHECK"] + ) return solutions_java_function diff --git a/source/scheduler/cdk/setup.py b/source/scheduler/cdk/setup.py index 430385c..6b5d938 100644 --- a/source/scheduler/cdk/setup.py +++ b/source/scheduler/cdk/setup.py @@ -47,7 +47,7 @@ def get_version(): "Click==8.1.3", "boto3==1.26.47", ], - python_requires=">=3.9", + python_requires=">=3.11", classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -57,6 +57,8 @@ def get_version(): "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Code Generators", "Topic :: Utilities", "Typing :: Typed", diff --git a/source/scheduler/common/__init__.py b/source/scheduler/common/__init__.py new file mode 100644 index 0000000..ef2f9eb --- /dev/null +++ b/source/scheduler/common/__init__.py @@ -0,0 +1,12 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### diff --git a/source/scheduler/common/setup.py b/source/scheduler/common/setup.py index fcf2355..22fdbc9 100644 --- a/source/scheduler/common/setup.py +++ b/source/scheduler/common/setup.py @@ -49,7 +49,7 @@ def get_version(): "click==8.1.3", "cronex==0.1.3.1", "boto3==1.26.47", - "requests==2.31.0", + "requests==2.32.4", "crhelper==2.0.11", "rich==12.6.0", ], @@ -57,7 +57,7 @@ def get_version(): [console_scripts] aws-solutions-scheduler=aws_solutions.scheduler.common.scripts.scheduler_cli:cli """, - python_requires=">=3.9", + python_requires=">=3.11", classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -67,6 +67,8 @@ def get_version(): "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Code Generators", "Topic :: Utilities", "Typing :: Typed", diff --git a/source/tests/aspects/test_personalize_app_stack.py b/source/tests/aspects/test_personalize_app_stack.py index d6ffa55..51066fd 100644 --- a/source/tests/aspects/test_personalize_app_stack.py +++ b/source/tests/aspects/test_personalize_app_stack.py @@ -67,11 +67,11 @@ def test_service_catalog_registry_application(synth_template): "Tags": { "SOLUTION_ID": "SO0170", "SOLUTION_NAME": "Maintaining Personalized Experiences with Machine Learning", - "SOLUTION_VERSION": "v1.4.4", + "SOLUTION_VERSION": "v1.4.5", "Solutions:ApplicationType": "AWS-Solutions", "Solutions:SolutionID": "SO0170", "Solutions:SolutionName": "Maintaining Personalized Experiences with Machine Learning", - "Solutions:SolutionVersion": "v1.4.4", + "Solutions:SolutionVersion": "v1.4.5", }, }, ) diff --git a/source/tests/cdk_solution_helper/aws_lambda/cfn_custom_resources/solution_metrics/test_metrics_resource.py b/source/tests/cdk_solution_helper/aws_lambda/cfn_custom_resources/solution_metrics/test_metrics_resource.py index 4786a5a..66d8364 100644 --- a/source/tests/cdk_solution_helper/aws_lambda/cfn_custom_resources/solution_metrics/test_metrics_resource.py +++ b/source/tests/cdk_solution_helper/aws_lambda/cfn_custom_resources/solution_metrics/test_metrics_resource.py @@ -36,7 +36,7 @@ def test_event(request): event = { "RequestType": request.param, - "ResourceProperties": {"Solution": "SOL0123", "Metric1": "Data1"}, + "ResourceProperties": {"Solution": "SOL0123", "Version": "v1.4.5","Metric1": "Data1"}, } yield event diff --git a/source/tests/cdk_solution_helper/aws_lambda/java/fixtures/java_sample/src/main/main.iml b/source/tests/cdk_solution_helper/aws_lambda/java/fixtures/java_sample/src/main/main.iml new file mode 100644 index 0000000..e4731c0 --- /dev/null +++ b/source/tests/cdk_solution_helper/aws_lambda/java/fixtures/java_sample/src/main/main.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/tests/cdk_solution_helper/aws_lambda/java/fixtures/java_sample/src/test/test1.iml b/source/tests/cdk_solution_helper/aws_lambda/java/fixtures/java_sample/src/test/test1.iml new file mode 100644 index 0000000..fbff544 --- /dev/null +++ b/source/tests/cdk_solution_helper/aws_lambda/java/fixtures/java_sample/src/test/test1.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/tests/cdk_solution_helper/aws_lambda/python/fixtures/pyproject.toml b/source/tests/cdk_solution_helper/aws_lambda/python/fixtures/pyproject.toml index fcf88d4..fbd7783 100644 --- a/source/tests/cdk_solution_helper/aws_lambda/python/fixtures/pyproject.toml +++ b/source/tests/cdk_solution_helper/aws_lambda/python/fixtures/pyproject.toml @@ -5,7 +5,7 @@ description = "" authors = ["AWS Solutions Builders"] [tool.poetry.dependencies] -python = "^3.9" +python = "^3.11" minimal = {path = "package"} [tool.poetry.dev-dependencies] diff --git a/source/tests/cdk_solution_helper/aws_lambda/python/test_function.py b/source/tests/cdk_solution_helper/aws_lambda/python/test_function.py index 098a86d..666c688 100644 --- a/source/tests/cdk_solution_helper/aws_lambda/python/test_function.py +++ b/source/tests/cdk_solution_helper/aws_lambda/python/test_function.py @@ -77,7 +77,7 @@ def test_function_has_default_role(function_synth): func = function_stack["Resources"]["TestFunction"] assert func["Type"] == "AWS::Lambda::Function" assert func["Properties"]["Handler"] == PYTHON_FUNCTION_NAME.split(".")[0] + "." + PYTHON_FUNCTION_HANDLER_NAME - assert func["Properties"]["Runtime"] == "python3.9" + assert func["Properties"]["Runtime"] == "python3.11" role = function_stack["Resources"][func["Properties"]["Role"]["Fn::GetAtt"][0]] assert role["Type"] == "AWS::IAM::Role" diff --git a/source/tests/conftest.py b/source/tests/conftest.py index 9e49897..e20aa72 100644 --- a/source/tests/conftest.py +++ b/source/tests/conftest.py @@ -101,7 +101,7 @@ def mock_lambda_init( props = FunctionProps( code=Code.from_inline("return"), handler=handler, - runtime=Runtime.PYTHON_3_9, + runtime=Runtime.PYTHON_3_11, **kwargs, ) jsii.create(Function, self, [scope, id, props]) @@ -112,7 +112,7 @@ def mock_layer_init(self, scope: Construct, id: str, *, code: Code, **kwargs) -> # override the runtime list for now, as well, to match above with TemporaryDirectory() as tmpdirname: kwargs["code"] = Code.from_asset(path=tmpdirname) - kwargs["compatible_runtimes"] = [Runtime.PYTHON_3_9] + kwargs["compatible_runtimes"] = [Runtime.PYTHON_3_11] props = LayerVersionProps(**kwargs) jsii.create(LayerVersion, self, [scope, id, props]) diff --git a/source/tests/test_deploy.py b/source/tests/test_deploy.py index 494ec85..fabd994 100644 --- a/source/tests/test_deploy.py +++ b/source/tests/test_deploy.py @@ -20,8 +20,8 @@ @pytest.fixture def build_stacks_for_buckets(): """Ensure parameter ordering is kept""" - from deploy import build_app - from deploy import solution as cdk_solution + from infrastructure.deploy import build_app + from infrastructure.deploy import solution as cdk_solution cdk_solution.reset() @@ -55,6 +55,12 @@ def test_parameters(build_stacks_for_buckets): ) +def test_personalize_bucket_notification_dependency(build_stacks_for_buckets): + stack = build_stacks_for_buckets + assert stack["Resources"]["PersonalizeBucketNotifications3328A32B"]["Type"] == "Custom::S3BucketNotifications" + assert "PersonalizeBucketPolicy5818C815" in stack["Resources"]["PersonalizeBucketNotifications3328A32B"]["DependsOn"] + + def test_personalize_bucket(build_stacks_for_buckets): stack = build_stacks_for_buckets personalize_bucket = stack["Resources"]["PersonalizeBucket"]