diff --git a/aws_lambda_builders/workflows/dotnet_clipackage/actions.py b/aws_lambda_builders/workflows/dotnet_clipackage/actions.py index 5ed686ca6..de7386d04 100644 --- a/aws_lambda_builders/workflows/dotnet_clipackage/actions.py +++ b/aws_lambda_builders/workflows/dotnet_clipackage/actions.py @@ -2,6 +2,7 @@ Actions for .NET dependency resolution with CLI Package """ +import threading import os import logging @@ -14,6 +15,8 @@ class GlobalToolInstallAction(BaseAction): + __lock = threading.Lock() + __tools_installed = False """ A Lambda Builder Action which installs the Amazon.Lambda.Tools .NET Core Global Tool @@ -28,15 +31,30 @@ def __init__(self, subprocess_dotnet): self.subprocess_dotnet = subprocess_dotnet def execute(self): - try: - LOG.debug("Installing Amazon.Lambda.Tools Global Tool") - self.subprocess_dotnet.run(["tool", "install", "-g", "Amazon.Lambda.Tools", "--ignore-failed-sources"]) - except DotnetCLIExecutionError as ex: - LOG.debug("Error installing probably due to already installed. Attempt to update to latest version.") + # run Amazon.Lambda.Tools update in sync block in case build is triggered in parallel + with GlobalToolInstallAction.__lock: + LOG.debug("Entered synchronized block for updating Amazon.Lambda.Tools") + + # check if Amazon.Lambda.Tools updated recently + if GlobalToolInstallAction.__tools_installed: + LOG.info("Skipping to update Amazon.Lambda.Tools install/update, since it is updated recently") + return + try: - self.subprocess_dotnet.run(["tool", "update", "-g", "Amazon.Lambda.Tools", "--ignore-failed-sources"]) + LOG.debug("Installing Amazon.Lambda.Tools Global Tool") + self.subprocess_dotnet.run(["tool", "install", "-g", "Amazon.Lambda.Tools", "--ignore-failed-sources"]) + GlobalToolInstallAction.__tools_installed = True except DotnetCLIExecutionError as ex: - raise ActionFailedError("Error configuring the Amazon.Lambda.Tools .NET Core Global Tool: " + str(ex)) + LOG.debug("Error installing probably due to already installed. Attempt to update to latest version.") + try: + self.subprocess_dotnet.run( + ["tool", "update", "-g", "Amazon.Lambda.Tools", "--ignore-failed-sources"] + ) + GlobalToolInstallAction.__tools_installed = True + except DotnetCLIExecutionError as ex: + raise ActionFailedError( + "Error configuring the Amazon.Lambda.Tools .NET Core Global Tool: " + str(ex) + ) class RunPackageAction(BaseAction): diff --git a/tests/unit/workflows/dotnet_clipackage/test_actions.py b/tests/unit/workflows/dotnet_clipackage/test_actions.py index 8b7688661..1df2694a3 100644 --- a/tests/unit/workflows/dotnet_clipackage/test_actions.py +++ b/tests/unit/workflows/dotnet_clipackage/test_actions.py @@ -1,4 +1,6 @@ from unittest import TestCase + +from concurrent.futures import ThreadPoolExecutor from mock import patch import os import platform @@ -8,6 +10,7 @@ from aws_lambda_builders.workflows.dotnet_clipackage.actions import GlobalToolInstallAction, RunPackageAction +@patch.object(GlobalToolInstallAction, "_GlobalToolInstallAction__tools_installed", False) class TestGlobalToolInstallAction(TestCase): @patch("aws_lambda_builders.workflows.dotnet_clipackage.dotnetcli.SubprocessDotnetCLI") def setUp(self, MockSubprocessDotnetCLI): @@ -42,6 +45,21 @@ def test_global_tool_update_failed(self): action = GlobalToolInstallAction(self.subprocess_dotnet) self.assertRaises(ActionFailedError, action.execute) + def test_global_tool_parallel(self): + actions = [ + GlobalToolInstallAction(self.subprocess_dotnet), + GlobalToolInstallAction(self.subprocess_dotnet), + GlobalToolInstallAction(self.subprocess_dotnet), + ] + + with ThreadPoolExecutor() as executor: + for action in actions: + executor.submit(action.execute) + + self.subprocess_dotnet.run.assert_called_once_with( + ["tool", "install", "-g", "Amazon.Lambda.Tools", "--ignore-failed-sources"] + ) + class TestRunPackageAction(TestCase): @patch("aws_lambda_builders.workflows.dotnet_clipackage.dotnetcli.SubprocessDotnetCLI")