diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f8326d3a..efbe7323cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ CHANGELOG - Upgrade Flask to ~=3.1.0 (from >=2.2.5,<2.3). - Upgrade Werkzeug to ~=3.1 (from ~=2.0) to address [CVE-2024-34069](https://nvd.nist.gov/vuln/detail/cve-2024-34069). +**BUG FIXES** +- Reduce EFA installation time for Ubuntu by ~20 minutes by only holding kernel packages for the installed kernel. +- Add GetFunction and GetPolicy permissions to PClusterBuildImageCleanupRole to prevent AccessDenied errors during build image stack deletion. + + 3.14.0 ------ diff --git a/cli/src/pcluster/constants.py b/cli/src/pcluster/constants.py index 194ae3d2c9..c561007c35 100644 --- a/cli/src/pcluster/constants.py +++ b/cli/src/pcluster/constants.py @@ -343,7 +343,7 @@ class Operation(Enum): PCLUSTER_BUILD_IMAGE_CLEANUP_ROLE_PREFIX = "PClusterBuildImageCleanupRole" # Tag key & expected revision (increment when policy widens) -PCLUSTER_BUILD_IMAGE_CLEANUP_ROLE_REVISION = 1 +PCLUSTER_BUILD_IMAGE_CLEANUP_ROLE_REVISION = 2 PCLUSTER_BUILD_IMAGE_CLEANUP_ROLE_BOOTSTRAP_TAG_KEY = "parallelcluster:build-image-cleanup-role-bootstrapped" P6E_GB200 = "p6e-gb200" diff --git a/cli/src/pcluster/imagebuilder_utils.py b/cli/src/pcluster/imagebuilder_utils.py index 9057231fd3..858cc27f15 100644 --- a/cli/src/pcluster/imagebuilder_utils.py +++ b/cli/src/pcluster/imagebuilder_utils.py @@ -140,7 +140,12 @@ def _expected_inline_policy(account_id: str, partition: str): {"Action": "ec2:CreateTags", "Resource": f"arn:{partition}:ec2:*::image/*", "Effect": "Allow"}, {"Action": "tag:TagResources", "Resource": "*", "Effect": "Allow"}, { - "Action": ["lambda:DeleteFunction", "lambda:RemovePermission"], + "Action": [ + "lambda:DeleteFunction", + "lambda:RemovePermission", + "lambda:GetFunction", + "lambda:GetPolicy", + ], "Resource": f"arn:{partition}:lambda:*:{account_id}:function:ParallelClusterImage-*", "Effect": "Allow", }, diff --git a/cli/tests/pcluster/cli/test_build_image.py b/cli/tests/pcluster/cli/test_build_image.py index fe42fd003b..1e920f34f2 100644 --- a/cli/tests/pcluster/cli/test_build_image.py +++ b/cli/tests/pcluster/cli/test_build_image.py @@ -283,19 +283,33 @@ def test_ensure_default_build_image_stack_cleanup_role_permission_denied(self, a aws_api_mock.iam.tag_role.assert_not_called() @pytest.mark.parametrize( - "account_id, partition", + "account_id, partition, actions", [ - ("123456789012", "aws"), - ("000000000000", "aws-us-gov"), + ( + "123456789012", + "aws", + ["lambda:DeleteFunction", "lambda:RemovePermission", "lambda:GetFunction", "lambda:GetPolicy"], + ), + ( + "000000000000", + "aws-us-gov", + ["lambda:DeleteFunction", "lambda:RemovePermission", "lambda:GetFunction", "lambda:GetPolicy"], + ), ], ) - def test_expected_inline_policy_dynamic_fields(self, account_id, partition): + def test_expected_inline_policy_dynamic_fields(self, account_id, partition, actions): raw = _expected_inline_policy(account_id, partition) policy = json.loads(raw) assert policy["Version"] == "2012-10-17" assert len(policy["Statement"]) == 13 for statement in policy["Statement"]: resources = statement["Resource"] + action = statement["Action"] + action = action if isinstance(action, list) else [action] + for act in action: + if act in actions: + actions.remove(act) + resources = resources if isinstance(resources, list) else [resources] for res in resources: if res == "*": @@ -303,6 +317,8 @@ def test_expected_inline_policy_dynamic_fields(self, account_id, partition): assert f"arn:{partition}" in res if not res == f"arn:{partition}:ec2:*::image/*": assert f":{account_id}:" in res + if len(actions) != 0: + raise AssertionError(f"Actions {actions} are not in the policy") def _build_args(self, args): args = [[k, v] if v is not None else [k] for k, v in args.items()]