diff --git a/src/DependencyManagement/DependencyManager.cs b/src/DependencyManagement/DependencyManager.cs index 28f4c59b..71caf2bd 100644 --- a/src/DependencyManagement/DependencyManager.cs +++ b/src/DependencyManagement/DependencyManager.cs @@ -105,6 +105,11 @@ internal string Initialize(ILogger logger) return null; } + if (WorkerEnvironment.IsLinuxConsumptionOnLegion()) + { + throw new NotSupportedException(PowerShellWorkerStrings.ManagedDependenciesIsNotSupportedOnLegion); + } + _currentSnapshotPath = _installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled() ?? _storage.CreateNewSnapshotPath(); diff --git a/src/DependencyManagement/ManagedDependenciesPathDetector.cs b/src/DependencyManagement/ManagedDependenciesPathDetector.cs index 0b575aaa..006f621d 100644 --- a/src/DependencyManagement/ManagedDependenciesPathDetector.cs +++ b/src/DependencyManagement/ManagedDependenciesPathDetector.cs @@ -10,10 +10,6 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement { internal static class ManagedDependenciesPathDetector { - // Environment variables to help figure out if we are running in Azure. - private const string AzureWebsiteInstanceId = "WEBSITE_INSTANCE_ID"; - private const string ContainerName = "CONTAINER_NAME"; - private const string HomeDriveName = "HOME"; private const string DataFolderName = "data"; @@ -31,7 +27,7 @@ internal static class ManagedDependenciesPathDetector public static string GetManagedDependenciesPath(string functionAppRootPath) { // If we are running in Azure App Service or Linux Consumption use the 'HOME\data' path. - if (IsAppService() || IsLinuxConsumption()) + if (WorkerEnvironment.IsAppService() || WorkerEnvironment.IsLinuxConsumption()) { var homeDriveVariable = Environment.GetEnvironmentVariable(HomeDriveName); if (string.IsNullOrEmpty(homeDriveVariable)) @@ -48,15 +44,5 @@ public static string GetManagedDependenciesPath(string functionAppRootPath) string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.DoNotVerify); return Path.Combine(appDataFolder, AzureFunctionsFolderName, functionAppName, ManagedDependenciesFolderName); } - - private static bool IsAppService() - { - return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(AzureWebsiteInstanceId)); - } - - private static bool IsLinuxConsumption() - { - return !IsAppService() && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(ContainerName)); - } } } diff --git a/src/DependencyManagement/WorkerEnvironment.cs b/src/DependencyManagement/WorkerEnvironment.cs new file mode 100644 index 00000000..ba6cbe9e --- /dev/null +++ b/src/DependencyManagement/WorkerEnvironment.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement +{ + /// + /// Hold information about the environment. + /// + internal static class WorkerEnvironment + { + // Environment variables to help figure out if we are running in Azure. + private const string AzureWebsiteInstanceId = "WEBSITE_INSTANCE_ID"; + private const string ContainerName = "CONTAINER_NAME"; + private const string LegionServiceHost = "LEGION_SERVICE_HOST"; + + public static bool IsAppService() + { + return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(AzureWebsiteInstanceId)); + } + + public static bool IsLinuxConsumption() + { + return !IsAppService() && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(ContainerName)); + } + + public static bool IsLinuxConsumptionOnLegion() + { + return !IsAppService() && + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(ContainerName)) && + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(LegionServiceHost)); + } + } +} diff --git a/src/resources/PowerShellWorkerStrings.resx b/src/resources/PowerShellWorkerStrings.resx index 89563720..0685d7b4 100644 --- a/src/resources/PowerShellWorkerStrings.resx +++ b/src/resources/PowerShellWorkerStrings.resx @@ -376,4 +376,7 @@ Utilizing external Durable Functions SDK: '{0}'. + + Managed Dependencies is not supported in Linux Consumption on Legion. Please remove all module references from requirements.psd1 and include the function app dependencies with the function app content. For more information, please see https://aka.ms/functions-powershell-include-modules. + \ No newline at end of file diff --git a/test/Unit/DependencyManagement/DependencyManagerTests.cs b/test/Unit/DependencyManagement/DependencyManagerTests.cs index e6249468..fbe8a13c 100644 --- a/test/Unit/DependencyManagement/DependencyManagerTests.cs +++ b/test/Unit/DependencyManagement/DependencyManagerTests.cs @@ -50,6 +50,72 @@ public void Initialize_ReturnsNewSnapshotPath_WhenNoAcceptableDependencyVersions } } + [Fact] + public void Initialize_Throws_WhenDependenciesAreDefinedInRequirementsPsd1_OnLegion() + { + const string ContainerName = "CONTAINER_NAME"; + const string LegionServiceHost = "LEGION_SERVICE_HOST"; + const string AzureWebsiteInstanceId = "WEBSITE_INSTANCE_ID"; + + try + { + Environment.SetEnvironmentVariable(AzureWebsiteInstanceId, null); + Environment.SetEnvironmentVariable(ContainerName, "MY_CONTAINER_NAME"); + Environment.SetEnvironmentVariable(LegionServiceHost, "MY_LEGION_SERVICE_HOST"); + + _mockStorage.Setup(_ => _.GetDependencies()).Returns(GetAnyNonEmptyDependencyManifestEntries()); + + using (var dependencyManager = CreateDependencyManagerWithMocks()) + { + var caughtException = Assert.Throws( + () => dependencyManager.Initialize(_mockLogger.Object)); + + Assert.Contains("Managed Dependencies is not supported in Linux Consumption on Legion.", caughtException.Message); + Assert.Contains("https://aka.ms/functions-powershell-include-modules", caughtException.Message); + } + } + + finally + { + Environment.SetEnvironmentVariable(ContainerName, null); + Environment.SetEnvironmentVariable(LegionServiceHost, null); + } + } + + [Fact] + public void Initialize_NoDependenciesOnRequirementsPsd1_OnLegion_DoesNotThrow() + { + const string ContainerName = "CONTAINER_NAME"; + const string LegionServiceHost = "LEGION_SERVICE_HOST"; + const string AzureWebsiteInstanceId = "WEBSITE_INSTANCE_ID"; + + try + { + Environment.SetEnvironmentVariable(AzureWebsiteInstanceId, null); + Environment.SetEnvironmentVariable(ContainerName, "MY_CONTAINER_NAME"); + Environment.SetEnvironmentVariable(LegionServiceHost, "MY_LEGION_SERVICE_HOST"); + + _mockStorage.Setup(_ => _.GetDependencies()).Returns(new DependencyManifestEntry[0]); + + using (var dependencyManager = CreateDependencyManagerWithMocks()) + { + var dependenciesPath = dependencyManager.Initialize(_mockLogger.Object); + + Assert.Null(dependenciesPath); + VerifyMessageLogged(LogLevel.Warning, PowerShellWorkerStrings.FunctionAppDoesNotHaveRequiredModulesToInstall, expectedIsUserLog: true); + + _mockBackgroundDependencySnapshotMaintainer.VerifyNoOtherCalls(); + _mockNewerDependencySnapshotDetector.VerifyNoOtherCalls(); + } + } + + finally + { + Environment.SetEnvironmentVariable(ContainerName, null); + Environment.SetEnvironmentVariable(LegionServiceHost, null); + } + } + [Fact] public void Initialize_ReturnsExistingSnapshotPath_WhenAcceptableDependencyVersionsAlreadyInstalled() {