From b60badab8f063ede84d04a671566141117678740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Tue, 27 Aug 2024 09:16:10 +0000 Subject: [PATCH 1/4] Adds support to delete old versions when successful deployment --- .../lib/sync/flows/alias_version_sync_flow.py | 16 ++++++++++ samcli/lib/sync/flows/function_sync_flow.py | 2 ++ .../flows/test_alias_version_sync_flow.py | 30 +++++++++++++++---- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/samcli/lib/sync/flows/alias_version_sync_flow.py b/samcli/lib/sync/flows/alias_version_sync_flow.py index f3a270b69d..63c3e4ba7e 100644 --- a/samcli/lib/sync/flows/alias_version_sync_flow.py +++ b/samcli/lib/sync/flows/alias_version_sync_flow.py @@ -23,12 +23,14 @@ class AliasVersionSyncFlow(SyncFlow): _function_identifier: str _alias_name: str + _delete_old_alias: bool _lambda_client: Any def __init__( self, function_identifier: str, alias_name: str, + delete_old_alias: bool, build_context: "BuildContext", deploy_context: "DeployContext", sync_context: "SyncContext", @@ -64,6 +66,7 @@ def __init__( self._function_identifier = function_identifier self._alias_name = alias_name self._lambda_client = None + self._delete_old_alias = delete_old_alias @property def sync_state_identifier(self) -> str: @@ -90,6 +93,7 @@ def compare_remote(self) -> bool: def sync(self) -> None: function_physical_id = self.get_physical_id(self._function_identifier) + current_alias_version = self._get_version_alias_if_exists() version = self._lambda_client.publish_version(FunctionName=function_physical_id).get("Version") self._local_sha = str_checksum(str(version), hashlib.sha256()) LOG.debug("%sCreated new function version: %s", self.log_prefix, version) @@ -97,6 +101,10 @@ def sync(self) -> None: self._lambda_client.update_alias( FunctionName=function_physical_id, Name=self._alias_name, FunctionVersion=version ) + if self._delete_old_alias and current_alias_version: + function_name_w_version = "{}:{}".format(function_physical_id, current_alias_version) + self._lambda_client.delete_function(FunctionName=function_name_w_version) + def gather_dependencies(self) -> List[SyncFlow]: return [] @@ -107,3 +115,11 @@ def _get_resource_api_calls(self) -> List[ResourceAPICall]: def _equality_keys(self) -> Any: """Combination of function identifier and alias name can used to identify each unique SyncFlow""" return self._function_identifier, self._alias_name + + def _get_version_alias_if_exists(self) -> Optional[str]: + try: + return str(self._lambda_client.get_alias(FunctionName=self.get_physical_id(self._function_identifier), + Name=self._alias_name) + .get("FunctionVersion")) + except self._lambda_client.exceptions.ResourceNotFoundException: + return None \ No newline at end of file diff --git a/samcli/lib/sync/flows/function_sync_flow.py b/samcli/lib/sync/flows/function_sync_flow.py index bf007eb71f..449b4f0aa4 100644 --- a/samcli/lib/sync/flows/function_sync_flow.py +++ b/samcli/lib/sync/flows/function_sync_flow.py @@ -109,11 +109,13 @@ def gather_dependencies(self) -> List[SyncFlow]: raise FunctionNotFound(f"Unable to find function {self._function_identifier}") auto_publish_alias_name = function_resource.get("Properties", dict()).get("AutoPublishAlias", None) + auto_delete_old_alias = function_resource.get("Properties", dict()).get("AutoDeleteOldAlias", False) if auto_publish_alias_name: sync_flows.append( AliasVersionSyncFlow( self._function_identifier, auto_publish_alias_name, + auto_delete_old_alias, self._build_context, self._deploy_context, self._sync_context, diff --git a/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py b/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py index 615a47672c..36c2b20006 100644 --- a/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py +++ b/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py @@ -1,19 +1,17 @@ -import os import hashlib - -from samcli.lib.sync.sync_flow import SyncFlow from unittest import TestCase -from unittest.mock import ANY, MagicMock, call, mock_open, patch +from unittest.mock import MagicMock, patch from samcli.lib.sync.flows.alias_version_sync_flow import AliasVersionSyncFlow from samcli.lib.utils.hash import str_checksum class TestAliasVersionSyncFlow(TestCase): - def create_sync_flow(self): + def create_sync_flow(self, delete_old_alias = False): sync_flow = AliasVersionSyncFlow( "Function1", "Alias1", + delete_old_alias, build_context=MagicMock(), deploy_context=MagicMock(), sync_context=MagicMock(), @@ -76,3 +74,25 @@ def test_local_sha(self, session_mock): sync_flow.sync() self.assertEqual(sync_flow._local_sha, str_checksum("2", hashlib.sha256())) + + @patch("samcli.lib.sync.sync_flow.Session") + def test_delete_old_alias(self, session_mock): + sync_flow = self.create_sync_flow(True) + + sync_flow.get_physical_id = MagicMock() + sync_flow.get_physical_id.return_value = "PhysicalFunction1" + + sync_flow.set_up() + + sync_flow._lambda_client.get_alias.return_value = {"FunctionVersion": "1"} + sync_flow._lambda_client.publish_version.return_value = {"Version": "2"} + + sync_flow.sync() + + sync_flow._lambda_client.publish_version.assert_called_once_with(FunctionName="PhysicalFunction1") + sync_flow._lambda_client.update_alias.assert_called_once_with( + FunctionName="PhysicalFunction1", Name="Alias1", FunctionVersion="2" + ) + sync_flow._lambda_client.delete_function.assert_called_once_with( + FunctionName="{}:{}".format("PhysicalFunction1", "1") + ) From 3c7a0606fbc81fbefe42214b9cf2c4c497faff3d Mon Sep 17 00:00:00 2001 From: Luis Duarte Date: Fri, 1 Nov 2024 11:55:27 +0000 Subject: [PATCH 2/4] Fetch AutoDeleteOldAlias from Metadata instead of Properties --- samcli/lib/sync/flows/function_sync_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samcli/lib/sync/flows/function_sync_flow.py b/samcli/lib/sync/flows/function_sync_flow.py index 449b4f0aa4..8fb1d97225 100644 --- a/samcli/lib/sync/flows/function_sync_flow.py +++ b/samcli/lib/sync/flows/function_sync_flow.py @@ -109,7 +109,7 @@ def gather_dependencies(self) -> List[SyncFlow]: raise FunctionNotFound(f"Unable to find function {self._function_identifier}") auto_publish_alias_name = function_resource.get("Properties", dict()).get("AutoPublishAlias", None) - auto_delete_old_alias = function_resource.get("Properties", dict()).get("AutoDeleteOldAlias", False) + auto_delete_old_alias = function_resource.get("Metadata", dict()).get("AutoDeleteOldAlias", False) if auto_publish_alias_name: sync_flows.append( AliasVersionSyncFlow( From f154b4b3d6eaaaf82f181d9d0a10a4e95c9cbb83 Mon Sep 17 00:00:00 2001 From: Luis Duarte Date: Fri, 1 Nov 2024 12:19:48 +0000 Subject: [PATCH 3/4] Address PR Comments --- .../lib/sync/flows/alias_version_sync_flow.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/samcli/lib/sync/flows/alias_version_sync_flow.py b/samcli/lib/sync/flows/alias_version_sync_flow.py index 63c3e4ba7e..34d733a68f 100644 --- a/samcli/lib/sync/flows/alias_version_sync_flow.py +++ b/samcli/lib/sync/flows/alias_version_sync_flow.py @@ -27,15 +27,15 @@ class AliasVersionSyncFlow(SyncFlow): _lambda_client: Any def __init__( - self, - function_identifier: str, - alias_name: str, - delete_old_alias: bool, - build_context: "BuildContext", - deploy_context: "DeployContext", - sync_context: "SyncContext", - physical_id_mapping: Dict[str, str], - stacks: Optional[List[Stack]] = None, + self, + function_identifier: str, + alias_name: str, + delete_old_alias: bool, + build_context: "BuildContext", + deploy_context: "DeployContext", + sync_context: "SyncContext", + physical_id_mapping: Dict[str, str], + stacks: Optional[List[Stack]] = None, ): """ Parameters @@ -102,9 +102,9 @@ def sync(self) -> None: FunctionName=function_physical_id, Name=self._alias_name, FunctionVersion=version ) if self._delete_old_alias and current_alias_version: - function_name_w_version = "{}:{}".format(function_physical_id, current_alias_version) - self._lambda_client.delete_function(FunctionName=function_name_w_version) - + formatted_function_name_version = "{}:{}".format(function_physical_id, current_alias_version) + LOG.debug("%Deleting Old Version Alias: %s", self.log_prefix, current_alias_version) + self._lambda_client.delete_function(FunctionName=formatted_function_name_version) def gather_dependencies(self) -> List[SyncFlow]: return [] @@ -119,7 +119,7 @@ def _equality_keys(self) -> Any: def _get_version_alias_if_exists(self) -> Optional[str]: try: return str(self._lambda_client.get_alias(FunctionName=self.get_physical_id(self._function_identifier), - Name=self._alias_name) - .get("FunctionVersion")) + Name=self._alias_name) + .get("FunctionVersion")) except self._lambda_client.exceptions.ResourceNotFoundException: - return None \ No newline at end of file + return None From 0698b03ea546b5f26bca7066d4942c117f0bd429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Fri, 8 Nov 2024 14:15:49 +0000 Subject: [PATCH 4/4] Fix Formatting --- .../lib/sync/flows/alias_version_sync_flow.py | 26 ++++++++++--------- .../flows/test_alias_version_sync_flow.py | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/samcli/lib/sync/flows/alias_version_sync_flow.py b/samcli/lib/sync/flows/alias_version_sync_flow.py index 34d733a68f..54830be1e7 100644 --- a/samcli/lib/sync/flows/alias_version_sync_flow.py +++ b/samcli/lib/sync/flows/alias_version_sync_flow.py @@ -27,15 +27,15 @@ class AliasVersionSyncFlow(SyncFlow): _lambda_client: Any def __init__( - self, - function_identifier: str, - alias_name: str, - delete_old_alias: bool, - build_context: "BuildContext", - deploy_context: "DeployContext", - sync_context: "SyncContext", - physical_id_mapping: Dict[str, str], - stacks: Optional[List[Stack]] = None, + self, + function_identifier: str, + alias_name: str, + delete_old_alias: bool, + build_context: "BuildContext", + deploy_context: "DeployContext", + sync_context: "SyncContext", + physical_id_mapping: Dict[str, str], + stacks: Optional[List[Stack]] = None, ): """ Parameters @@ -118,8 +118,10 @@ def _equality_keys(self) -> Any: def _get_version_alias_if_exists(self) -> Optional[str]: try: - return str(self._lambda_client.get_alias(FunctionName=self.get_physical_id(self._function_identifier), - Name=self._alias_name) - .get("FunctionVersion")) + return str( + self._lambda_client.get_alias( + FunctionName=self.get_physical_id(self._function_identifier), Name=self._alias_name + ).get("FunctionVersion") + ) except self._lambda_client.exceptions.ResourceNotFoundException: return None diff --git a/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py b/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py index 36c2b20006..d4684b670a 100644 --- a/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py +++ b/tests/unit/lib/sync/flows/test_alias_version_sync_flow.py @@ -7,7 +7,7 @@ class TestAliasVersionSyncFlow(TestCase): - def create_sync_flow(self, delete_old_alias = False): + def create_sync_flow(self, delete_old_alias=False): sync_flow = AliasVersionSyncFlow( "Function1", "Alias1",