From 25b4f8069d6f08ae3bfd5a356f9303fb9cd9ca3b Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 13 Mar 2020 16:07:16 -0700 Subject: [PATCH] Add environment variables for Launcher path and args in ANCM (#19797) --- .../AspNetCore/ShimOptions.cpp | 5 ++ .../CommonLib/ConfigurationSection.h | 2 + .../InProcessOptions.cpp | 6 ++ .../requesthandler_config.cpp | 56 ++++++++++++++----- .../Inprocess/StartupTests.cs | 33 +++++++++++ 5 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp index 208d4a1eaa..353b0be788 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp @@ -54,6 +54,11 @@ ShimOptions::ShimOptions(const ConfigurationSource &configurationSource) : .value_or(environmentVariables[CS_ASPNETCORE_ENVIRONMENT]); const auto dotnetEnvironment = Environment::GetEnvironmentVariableValue(CS_DOTNET_ENVIRONMENT) .value_or(environmentVariables[CS_DOTNET_ENVIRONMENT]); + // We prefer the environment variables for LAUNCHER_PATH and LAUNCHER_ARGS + m_strProcessPath = Environment::GetEnvironmentVariableValue(CS_ANCM_LAUNCHER_PATH) + .value_or(m_strProcessPath); + m_strArguments = Environment::GetEnvironmentVariableValue(CS_ANCM_LAUNCHER_ARGS) + .value_or(m_strArguments); auto detailedErrorsEnabled = equals_ignore_case(L"1", detailedErrors) || equals_ignore_case(L"true", detailedErrors); auto aspnetCoreEnvironmentEnabled = equals_ignore_case(L"Development", aspnetCoreEnvironment); diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h index ae02dd3faa..8c9cece3e3 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h @@ -33,6 +33,8 @@ #define CS_ASPNETCORE_DETAILEDERRORS L"ASPNETCORE_DETAILEDERRORS" #define CS_ASPNETCORE_ENVIRONMENT L"ASPNETCORE_ENVIRONMENT" #define CS_DOTNET_ENVIRONMENT L"DOTNET_ENVIRONMENT" +#define CS_ANCM_LAUNCHER_PATH L"ANCM_LAUNCHER_PATH" +#define CS_ANCM_LAUNCHER_ARGS L"ANCM_LAUNCHER_ARGS" class ConfigurationSection: NonCopyable { diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp index 01ec10f6f6..c2ff5e0a7d 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp @@ -4,6 +4,7 @@ #include "InProcessOptions.h" #include "InvalidOperationException.h" #include "EventLog.h" +#include "Environment.h" HRESULT InProcessOptions::Create( IHttpServer& pServer, @@ -51,6 +52,11 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc auto const aspNetCoreSection = configurationSource.GetRequiredSection(CS_ASPNETCORE_SECTION); m_strArguments = aspNetCoreSection->GetString(CS_ASPNETCORE_PROCESS_ARGUMENTS).value_or(CS_ASPNETCORE_PROCESS_ARGUMENTS_DEFAULT); m_strProcessPath = aspNetCoreSection->GetRequiredString(CS_ASPNETCORE_PROCESS_EXE_PATH); + // We prefer the environment variables for LAUNCHER_PATH and LAUNCHER_ARGS + m_strProcessPath = Environment::GetEnvironmentVariableValue(CS_ANCM_LAUNCHER_PATH) + .value_or(m_strProcessPath); + m_strArguments = Environment::GetEnvironmentVariableValue(CS_ANCM_LAUNCHER_ARGS) + .value_or(m_strArguments); m_fStdoutLogEnabled = aspNetCoreSection->GetRequiredBool(CS_ASPNETCORE_STDOUT_LOG_ENABLED); m_struStdoutLogFile = aspNetCoreSection->GetRequiredString(CS_ASPNETCORE_STDOUT_LOG_FILE); m_fDisableStartUpErrorPage = aspNetCoreSection->GetRequiredBool(CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE); diff --git a/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp b/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp index 437e3b1596..c32bc51ced 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp @@ -7,6 +7,7 @@ #include "environmentvariablehash.h" #include "exceptions.h" #include "config_utility.h" +#include "Environment.h" REQUESTHANDLER_CONFIG::~REQUESTHANDLER_CONFIG() { @@ -101,6 +102,8 @@ REQUESTHANDLER_CONFIG::Populate( BSTR bstrBasicAuthSection = NULL; BSTR bstrAnonymousAuthSection = NULL; BSTR bstrAspNetCoreSection = NULL; + std::optional launcherPathEnv; + std::optional launcherArgsEnv; pAdminManager = pHttpServer->GetAdminManager(); try @@ -248,12 +251,47 @@ REQUESTHANDLER_CONFIG::Populate( goto Finished; } - hr = GetElementStringProperty(pAspNetCoreElement, - CS_ASPNETCORE_PROCESS_EXE_PATH, - &m_struProcessPath); - if (FAILED(hr)) + // We prefer the environment variables for LAUNCHER_PATH and LAUNCHER_ARGS + try { - goto Finished; + launcherPathEnv = Environment::GetEnvironmentVariableValue(CS_ANCM_LAUNCHER_PATH); + launcherArgsEnv = Environment::GetEnvironmentVariableValue(CS_ANCM_LAUNCHER_ARGS); + } + catch(...) + { + FINISHED_IF_FAILED(E_FAIL); + } + + if (launcherPathEnv.has_value()) + { + hr = m_struProcessPath.Copy(launcherPathEnv.value().c_str()); + FINISHED_IF_FAILED(hr); + } + else + { + hr = GetElementStringProperty(pAspNetCoreElement, + CS_ASPNETCORE_PROCESS_EXE_PATH, + &m_struProcessPath); + if (FAILED(hr)) + { + goto Finished; + } + } + + if (launcherArgsEnv.has_value()) + { + hr = m_struArguments.Copy(launcherArgsEnv.value().c_str()); + FINISHED_IF_FAILED(hr); + } + else + { + hr = GetElementStringProperty(pAspNetCoreElement, + CS_ASPNETCORE_PROCESS_ARGUMENTS, + &m_struArguments); + if (FAILED(hr)) + { + goto Finished; + } } hr = GetElementStringProperty(pAspNetCoreElement, @@ -281,14 +319,6 @@ REQUESTHANDLER_CONFIG::Populate( goto Finished; } - hr = GetElementStringProperty(pAspNetCoreElement, - CS_ASPNETCORE_PROCESS_ARGUMENTS, - &m_struArguments); - if (FAILED(hr)) - { - goto Finished; - } - hr = GetElementDWORDProperty(pAspNetCoreElement, CS_ASPNETCORE_RAPID_FAILS_PER_MINUTE, &m_dwRapidFailsPerMinute); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 9aae26fe34..c030a87161 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -880,6 +880,39 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess Assert.True(result.IsSuccessStatusCode); } + [ConditionalTheory] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + [RequiresNewShim] + [RequiresNewHandler] + [InlineData(HostingModel.InProcess)] + [InlineData(HostingModel.OutOfProcess)] + public async Task EnvironmentVariableForLauncherPathIsPreferred(HostingModel hostingModel) + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(hostingModel); + + deploymentParameters.EnvironmentVariables["ANCM_LAUNCHER_PATH"] = _dotnetLocation; + deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", "nope")); + + await StartAsync(deploymentParameters); + } + + [ConditionalTheory] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + [RequiresNewShim] + [RequiresNewHandler] + [InlineData(HostingModel.InProcess)] + [InlineData(HostingModel.OutOfProcess)] + public async Task EnvironmentVariableForLauncherArgsIsPreferred(HostingModel hostingModel) + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(hostingModel); + using var publishedApp = await deploymentParameters.ApplicationPublisher.Publish(deploymentParameters, LoggerFactory.CreateLogger("test")); + + deploymentParameters.EnvironmentVariables["ANCM_LAUNCHER_ARGS"] = Path.ChangeExtension(Path.Combine(publishedApp.Path, deploymentParameters.ApplicationPublisher.ApplicationPath), ".dll"); + deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("arguments", "nope")); + + await StartAsync(deploymentParameters); + } + private static void VerifyDotnetRuntimeEventLog(IISDeploymentResult deploymentResult) { var entries = GetEventLogsFromDotnetRuntime(deploymentResult);