Merge pull request #1173 from dotnet-maestro-bot/merge/release/2.2-to-master
[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
commit
11514cd17f
|
|
@ -8,7 +8,7 @@ phases:
|
|||
afterBuild:
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Upload binlog
|
||||
condition: always()
|
||||
condition: eq(variables['system.pullrequest.isfork'], false)
|
||||
inputs:
|
||||
artifactName: logs
|
||||
artifactType: Container
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "GlobalVersionUtility.h"
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// throws runtime error if no request handler versions are installed.
|
||||
// Throw invalid_argument if any argument is null
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ PollingAppOfflineApplication::CheckAppOffline()
|
|||
}
|
||||
|
||||
|
||||
std::experimental::filesystem::path PollingAppOfflineApplication::GetAppOfflineLocation(IHttpApplication& pApplication)
|
||||
std::filesystem::path PollingAppOfflineApplication::GetAppOfflineLocation(IHttpApplication& pApplication)
|
||||
{
|
||||
return std::experimental::filesystem::path(pApplication.GetApplicationPhysicalPath()) / "app_offline.htm";
|
||||
return std::filesystem::path(pApplication.GetApplicationPhysicalPath()) / "app_offline.htm";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ public:
|
|||
void StopInternal(bool fServerInitiated) override { UNREFERENCED_PARAMETER(fServerInitiated); }
|
||||
|
||||
protected:
|
||||
std::experimental::filesystem::path m_appOfflineLocation;
|
||||
static std::experimental::filesystem::path GetAppOfflineLocation(IHttpApplication& pApplication);
|
||||
std::filesystem::path m_appOfflineLocation;
|
||||
static std::filesystem::path GetAppOfflineLocation(IHttpApplication& pApplication);
|
||||
|
||||
private:
|
||||
static const int c_appOfflineRefreshIntervalMS = 200;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
#include "HandleWrapper.h"
|
||||
#include "Environment.h"
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
//
|
||||
// Runs a standalone appliction.
|
||||
|
|
@ -98,7 +98,7 @@ HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
|
|||
}
|
||||
|
||||
BOOL
|
||||
HOSTFXR_UTILITY::IsDotnetExecutable(const std::experimental::filesystem::path & dotnetPath)
|
||||
HOSTFXR_UTILITY::IsDotnetExecutable(const std::filesystem::path & dotnetPath)
|
||||
{
|
||||
auto name = dotnetPath.filename();
|
||||
name.replace_extension("");
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public:
|
|||
static
|
||||
BOOL
|
||||
IsDotnetExecutable(
|
||||
_In_ const std::experimental::filesystem::path & dotnetPath
|
||||
_In_ const std::filesystem::path & dotnetPath
|
||||
);
|
||||
|
||||
static
|
||||
|
|
@ -74,25 +74,25 @@ public:
|
|||
);
|
||||
|
||||
static
|
||||
std::optional<std::experimental::filesystem::path>
|
||||
std::optional<std::filesystem::path>
|
||||
GetAbsolutePathToHostFxr(
|
||||
_In_ const std::experimental::filesystem::path & dotnetPath,
|
||||
_In_ const std::filesystem::path & dotnetPath,
|
||||
_In_ HANDLE hEventLog
|
||||
);
|
||||
|
||||
static
|
||||
std::optional<std::experimental::filesystem::path>
|
||||
std::optional<std::filesystem::path>
|
||||
GetAbsolutePathToDotnetFromProgramFiles();
|
||||
|
||||
static
|
||||
std::optional<std::experimental::filesystem::path>
|
||||
std::optional<std::filesystem::path>
|
||||
InvokeWhereToFindDotnet();
|
||||
|
||||
static
|
||||
std::optional<std::experimental::filesystem::path>
|
||||
std::optional<std::filesystem::path>
|
||||
GetAbsolutePathToDotnet(
|
||||
_In_ const std::experimental::filesystem::path & applicationPath,
|
||||
_In_ const std::experimental::filesystem::path & requestedPath
|
||||
_In_ const std::filesystem::path & applicationPath,
|
||||
_In_ const std::filesystem::path & requestedPath
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,380 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Web.Administration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the IIS website registered in the global applicationHost.config
|
||||
/// </summary>
|
||||
internal class IISApplication
|
||||
{
|
||||
internal const int ERROR_OBJECT_NOT_FOUND = unchecked((int)0x800710D8);
|
||||
internal const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
|
||||
|
||||
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(10);
|
||||
private static readonly TimeSpan _retryDelay = TimeSpan.FromMilliseconds(200);
|
||||
private readonly ServerManager _serverManager = new ServerManager();
|
||||
private readonly DeploymentParameters _deploymentParameters;
|
||||
private readonly ILogger _logger;
|
||||
private readonly string _ancmVersion;
|
||||
private readonly string _ancmDllName;
|
||||
private readonly string _apphostConfigBackupPath;
|
||||
private static readonly string _apphostConfigPath = Path.Combine(
|
||||
Environment.SystemDirectory,
|
||||
"inetsrv",
|
||||
"config",
|
||||
"applicationhost.config");
|
||||
|
||||
public Process HostProcess { get; set; }
|
||||
|
||||
public IISApplication(DeploymentParameters deploymentParameters, ILogger logger)
|
||||
{
|
||||
_deploymentParameters = deploymentParameters;
|
||||
_logger = logger;
|
||||
_ancmVersion = deploymentParameters.AncmVersion.ToString();
|
||||
_ancmDllName = deploymentParameters.AncmVersion == AncmVersion.AspNetCoreModuleV2 ? "aspnetcorev2.dll" : "aspnetcore.dll";
|
||||
WebSiteName = CreateTestSiteName();
|
||||
AppPoolName = $"{WebSiteName}Pool";
|
||||
_apphostConfigBackupPath = Path.Combine(
|
||||
Environment.SystemDirectory,
|
||||
"inetsrv",
|
||||
"config",
|
||||
$"applicationhost.config.{WebSiteName}backup");
|
||||
}
|
||||
|
||||
public string WebSiteName { get; }
|
||||
|
||||
public string AppPoolName { get; }
|
||||
|
||||
public async Task StartIIS(Uri uri, string contentRoot)
|
||||
{
|
||||
// Backup currently deployed apphost.config file
|
||||
using (_logger.BeginScope("StartIIS"))
|
||||
{
|
||||
var port = uri.Port;
|
||||
if (port == 0)
|
||||
{
|
||||
throw new NotSupportedException("Cannot set port 0 for IIS.");
|
||||
}
|
||||
AddTemporaryAppHostConfig();
|
||||
|
||||
ConfigureAppPool(contentRoot);
|
||||
|
||||
ConfigureSite(contentRoot, port);
|
||||
|
||||
ConfigureAppHostConfig();
|
||||
|
||||
_serverManager.CommitChanges();
|
||||
|
||||
await WaitUntilSiteStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitUntilSiteStarted()
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
while (sw.Elapsed < _timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
var serverManager = new ServerManager();
|
||||
var appPool = serverManager.ApplicationPools.FirstOrDefault(s => s.Name.Equals(AppPoolName));
|
||||
var site = serverManager.Sites.FirstOrDefault(s => s.Name.Equals(WebSiteName));
|
||||
|
||||
if (site.State == ObjectState.Started)
|
||||
{
|
||||
var workerProcess = appPool.WorkerProcesses.SingleOrDefault();
|
||||
if (workerProcess != null)
|
||||
{
|
||||
HostProcess = Process.GetProcessById(workerProcess.ProcessId);
|
||||
_logger.LogInformation($"Site {WebSiteName} has started.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (site.State != ObjectState.Starting)
|
||||
{
|
||||
_logger.LogInformation($"Site hasn't started with state: {site.State.ToString()}");
|
||||
var state = site.Start();
|
||||
_logger.LogInformation($"Tried to start site, state: {state.ToString()}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (
|
||||
ex is DllNotFoundException ||
|
||||
ex is COMException &&
|
||||
(ex.HResult == ERROR_OBJECT_NOT_FOUND || ex.HResult == ERROR_SHARING_VIOLATION))
|
||||
{
|
||||
// Accessing the site.State property while the site
|
||||
// is starting up returns the COMException
|
||||
// The object identifier does not represent a valid object.
|
||||
// (Exception from HRESULT: 0x800710D8)
|
||||
// This also means the site is not started yet, so catch and retry
|
||||
// after waiting.
|
||||
}
|
||||
|
||||
await Task.Delay(_retryDelay);
|
||||
}
|
||||
|
||||
throw new TimeoutException($"IIS failed to start site.");
|
||||
}
|
||||
|
||||
public async Task StopAndDeleteAppPool()
|
||||
{
|
||||
if (string.IsNullOrEmpty(WebSiteName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StopSite();
|
||||
|
||||
StopAppPool();
|
||||
|
||||
_serverManager.CommitChanges();
|
||||
|
||||
await WaitUntilSiteStopped();
|
||||
|
||||
RestoreAppHostConfig();
|
||||
}
|
||||
|
||||
private async Task WaitUntilSiteStopped()
|
||||
{
|
||||
var site = _serverManager.Sites.Where(element => element.Name == WebSiteName).FirstOrDefault();
|
||||
if (site == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
while (sw.Elapsed < _timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (site.State == ObjectState.Stopped)
|
||||
{
|
||||
if (HostProcess.HasExited)
|
||||
{
|
||||
_logger.LogInformation($"Site {WebSiteName} has stopped successfully.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (COMException)
|
||||
{
|
||||
// Accessing the site.State property while the site
|
||||
// is shutdown down returns the COMException
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogWarning($"IIS has not stopped after {sw.Elapsed.TotalMilliseconds}");
|
||||
await Task.Delay(_retryDelay);
|
||||
}
|
||||
|
||||
throw new TimeoutException($"IIS failed to stop site {site}.");
|
||||
}
|
||||
|
||||
private void AddTemporaryAppHostConfig()
|
||||
{
|
||||
RetryFileOperation(() => File.Move(_apphostConfigPath, _apphostConfigBackupPath),
|
||||
e => _logger.LogWarning($"Failed to backup apphost.config: {e.Message}"));
|
||||
|
||||
_logger.LogInformation($"Backed up {_apphostConfigPath} to {_apphostConfigBackupPath}");
|
||||
|
||||
RetryFileOperation(
|
||||
() => File.WriteAllText(_apphostConfigPath, _deploymentParameters.ServerConfigTemplateContent ?? File.ReadAllText("IIS.config")),
|
||||
e => _logger.LogWarning($"Failed to copy IIS.config to apphost.config: {e.Message}"));
|
||||
|
||||
_logger.LogInformation($"Copied contents of IIS.config to {_apphostConfigPath}");
|
||||
}
|
||||
|
||||
private void RestoreAppHostConfig()
|
||||
{
|
||||
if (File.Exists(_apphostConfigPath))
|
||||
{
|
||||
RetryFileOperation(
|
||||
() => File.Delete(_apphostConfigPath),
|
||||
e => _logger.LogWarning($"Failed to delete file : {e.Message}"));
|
||||
}
|
||||
if (File.Exists(_apphostConfigBackupPath))
|
||||
{
|
||||
RetryFileOperation(
|
||||
() => File.Move(_apphostConfigBackupPath, _apphostConfigPath),
|
||||
e => _logger.LogError($"Failed to backup apphost.config: {e.Message}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Test failed to create backup config file, put a default one from IIS.config there instead.
|
||||
// An apphost.config file is required to be replaced because we use it for removing the app pool.
|
||||
RetryFileOperation(
|
||||
() => File.WriteAllText(_apphostConfigPath, File.ReadAllText("IIS.config")),
|
||||
e => _logger.LogWarning($"Failed to copy IIS.config to apphost.config: {e.Message}"));
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Restored {_apphostConfigPath}.");
|
||||
}
|
||||
|
||||
private void RetryFileOperation(Action retryBlock, Action<Exception> exceptionBlock)
|
||||
{
|
||||
RetryHelper.RetryOperation(retryBlock,
|
||||
exceptionBlock,
|
||||
retryCount: 10,
|
||||
retryDelayMilliseconds: 100);
|
||||
}
|
||||
|
||||
private ApplicationPool ConfigureAppPool(string contentRoot)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pool = _serverManager.ApplicationPools.Add(AppPoolName);
|
||||
pool.ProcessModel.IdentityType = ProcessModelIdentityType.LocalSystem;
|
||||
pool.ManagedRuntimeVersion = string.Empty;
|
||||
pool.StartMode = StartMode.AlwaysRunning;
|
||||
AddEnvironmentVariables(contentRoot, pool);
|
||||
|
||||
_logger.LogInformation($"Configured AppPool {AppPoolName}");
|
||||
return pool;
|
||||
}
|
||||
catch (COMException comException)
|
||||
{
|
||||
_logger.LogError(File.ReadAllText(_apphostConfigPath));
|
||||
throw comException;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddEnvironmentVariables(string contentRoot, ApplicationPool pool)
|
||||
{
|
||||
try
|
||||
{
|
||||
var envCollection = pool.GetCollection("environmentVariables");
|
||||
|
||||
foreach (var tuple in _deploymentParameters.EnvironmentVariables)
|
||||
{
|
||||
AddEnvironmentVariableToAppPool(envCollection, tuple.Key, tuple.Value);
|
||||
}
|
||||
}
|
||||
catch (COMException comException)
|
||||
{
|
||||
_logger.LogInformation($"Could not add environment variables to worker process: {comException.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddEnvironmentVariableToAppPool(ConfigurationElementCollection envCollection, string key, string value)
|
||||
{
|
||||
var addElement = envCollection.CreateElement("add");
|
||||
addElement["name"] = key;
|
||||
addElement["value"] = value;
|
||||
envCollection.Add(addElement);
|
||||
}
|
||||
|
||||
private Site ConfigureSite(string contentRoot, int port)
|
||||
{
|
||||
var site = _serverManager.Sites.Add(WebSiteName, contentRoot, port);
|
||||
site.Applications.Single().ApplicationPoolName = AppPoolName;
|
||||
_logger.LogInformation($"Configured Site {WebSiteName} with AppPool {AppPoolName}");
|
||||
return site;
|
||||
}
|
||||
|
||||
private Configuration ConfigureAppHostConfig()
|
||||
{
|
||||
var config = _serverManager.GetApplicationHostConfiguration();
|
||||
|
||||
SetGlobalModuleSection(config);
|
||||
|
||||
SetModulesSection(config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private void StopSite()
|
||||
{
|
||||
var site = _serverManager.Sites.Where(sites => sites.Name == WebSiteName).SingleOrDefault();
|
||||
|
||||
site.Stop();
|
||||
}
|
||||
|
||||
private void StopAppPool()
|
||||
{
|
||||
var appPool = _serverManager.ApplicationPools.Where(pool => pool.Name == AppPoolName).SingleOrDefault();
|
||||
|
||||
appPool.Stop();
|
||||
}
|
||||
|
||||
private void SetGlobalModuleSection(Configuration config)
|
||||
{
|
||||
var ancmFile = GetAncmLocation();
|
||||
|
||||
var globalModulesSection = config.GetSection("system.webServer/globalModules");
|
||||
var globalConfigElement = globalModulesSection
|
||||
.GetCollection()
|
||||
.Where(element => (string)element["name"] == _ancmVersion)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (globalConfigElement == null)
|
||||
{
|
||||
_logger.LogInformation($"Could not find {_ancmVersion} section in global modules; creating section.");
|
||||
var addElement = globalModulesSection.GetCollection().CreateElement("add");
|
||||
addElement["name"] = _ancmVersion;
|
||||
addElement["image"] = ancmFile;
|
||||
globalModulesSection.GetCollection().Add(addElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"Replacing {_ancmVersion} section in global modules with {ancmFile}");
|
||||
globalConfigElement["image"] = ancmFile;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetModulesSection(Configuration config)
|
||||
{
|
||||
var modulesSection = config.GetSection("system.webServer/modules");
|
||||
var moduleConfigElement = modulesSection.GetCollection().Where(element => (string)element["name"] == _ancmVersion).FirstOrDefault();
|
||||
if (moduleConfigElement == null)
|
||||
{
|
||||
_logger.LogInformation($"Could not find {_ancmVersion} section in modules; creating section.");
|
||||
var moduleElement = modulesSection.GetCollection().CreateElement("add");
|
||||
moduleElement["name"] = _ancmVersion;
|
||||
modulesSection.GetCollection().Add(moduleElement);
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateTestSiteName()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_deploymentParameters.SiteName))
|
||||
{
|
||||
return $"{_deploymentParameters.SiteName}{DateTime.Now.ToString("yyyyMMddHHmmss")}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"testsite{DateTime.Now.ToString("yyyyMMddHHmmss")}";
|
||||
}
|
||||
}
|
||||
|
||||
private string GetAncmLocation()
|
||||
{
|
||||
var dllRoot = AppContext.BaseDirectory;
|
||||
var arch = _deploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x64 ? $@"x64\{_ancmDllName}" : $@"x86\{_ancmDllName}";
|
||||
var ancmFile = Path.Combine(dllRoot, arch);
|
||||
if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile)))
|
||||
{
|
||||
ancmFile = Path.Combine(dllRoot, _ancmDllName);
|
||||
if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile)))
|
||||
{
|
||||
throw new FileNotFoundException("AspNetCoreModule could not be found.", ancmFile);
|
||||
}
|
||||
}
|
||||
|
||||
return ancmFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,39 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Web.Administration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||
{
|
||||
/// <summary>
|
||||
/// Deployer for IIS.
|
||||
/// </summary>
|
||||
public partial class IISDeployer : IISDeployerBase
|
||||
public class IISDeployer : IISDeployerBase
|
||||
{
|
||||
private IISApplication _application;
|
||||
internal const int ERROR_OBJECT_NOT_FOUND = unchecked((int)0x800710D8);
|
||||
internal const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
|
||||
internal const int ERROR_SERVICE_CANNOT_ACCEPT_CTRL = unchecked((int)0x80070425);
|
||||
|
||||
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(10);
|
||||
private static readonly TimeSpan _retryDelay = TimeSpan.FromMilliseconds(200);
|
||||
|
||||
private CancellationTokenSource _hostShutdownToken = new CancellationTokenSource();
|
||||
|
||||
private string _configPath;
|
||||
private string _debugLogFile;
|
||||
|
||||
public Process HostProcess { get; set; }
|
||||
|
||||
public IISDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
|
||||
: base(new IISDeploymentParameters(deploymentParameters), loggerFactory)
|
||||
{
|
||||
|
|
@ -29,14 +46,11 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_application != null)
|
||||
{
|
||||
_application.StopAndDeleteAppPool().GetAwaiter().GetResult();
|
||||
StopAndDeleteAppPool().GetAwaiter().GetResult();
|
||||
|
||||
TriggerHostShutdown(_hostShutdownToken);
|
||||
}
|
||||
TriggerHostShutdown(_hostShutdownToken);
|
||||
|
||||
GetLogsFromFile($"{_application.WebSiteName}.txt");
|
||||
GetLogsFromFile();
|
||||
|
||||
CleanPublishedOutput();
|
||||
InvokeUserApplicationCleanup();
|
||||
|
|
@ -50,17 +64,23 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
{
|
||||
StartTimer();
|
||||
|
||||
var contentRoot = string.Empty;
|
||||
if (string.IsNullOrEmpty(DeploymentParameters.ServerConfigTemplateContent))
|
||||
{
|
||||
DeploymentParameters.ServerConfigTemplateContent = File.ReadAllText("IIS.config");
|
||||
}
|
||||
|
||||
_application = new IISApplication(IISDeploymentParameters, Logger);
|
||||
|
||||
// For now, only support using published output
|
||||
DeploymentParameters.PublishApplicationBeforeDeployment = true;
|
||||
|
||||
// Do not override settings set on parameters
|
||||
if (!IISDeploymentParameters.HandlerSettings.ContainsKey("debugLevel") &&
|
||||
!IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
|
||||
{
|
||||
_debugLogFile = Path.GetTempFileName();
|
||||
IISDeploymentParameters.HandlerSettings["debugLevel"] = "4";
|
||||
IISDeploymentParameters.HandlerSettings["debugFile"] = _debugLogFile;
|
||||
}
|
||||
|
||||
if (DeploymentParameters.ApplicationType == ApplicationType.Portable)
|
||||
{
|
||||
DefaultWebConfigActions.Add(
|
||||
|
|
@ -69,28 +89,19 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
DotNetCommands.GetDotNetExecutable(DeploymentParameters.RuntimeArchitecture)));
|
||||
}
|
||||
|
||||
if (DeploymentParameters.PublishApplicationBeforeDeployment)
|
||||
{
|
||||
DotnetPublish();
|
||||
contentRoot = DeploymentParameters.PublishedApplicationRootPath;
|
||||
// Do not override settings set on parameters
|
||||
if (!IISDeploymentParameters.HandlerSettings.ContainsKey("debugLevel") &&
|
||||
!IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
|
||||
{
|
||||
var logFile = Path.Combine(contentRoot, $"{_application.WebSiteName}.txt");
|
||||
IISDeploymentParameters.HandlerSettings["debugLevel"] = "4";
|
||||
IISDeploymentParameters.HandlerSettings["debugFile"] = logFile;
|
||||
}
|
||||
|
||||
DefaultWebConfigActions.Add(WebConfigHelpers.AddOrModifyHandlerSection(
|
||||
key: "modules",
|
||||
value: DeploymentParameters.AncmVersion.ToString()));
|
||||
RunWebConfigActions(contentRoot);
|
||||
}
|
||||
DotnetPublish();
|
||||
var contentRoot = DeploymentParameters.PublishedApplicationRootPath;
|
||||
|
||||
DefaultWebConfigActions.Add(WebConfigHelpers.AddOrModifyHandlerSection(
|
||||
key: "modules",
|
||||
value: DeploymentParameters.AncmVersion.ToString()));
|
||||
|
||||
RunWebConfigActions(contentRoot);
|
||||
|
||||
var uri = TestUriHelper.BuildTestUri(ServerType.IIS, DeploymentParameters.ApplicationBaseUriHint);
|
||||
// To prevent modifying the IIS setup concurrently.
|
||||
await _application.StartIIS(uri, contentRoot);
|
||||
await StartIIS(uri, contentRoot);
|
||||
|
||||
// Warm up time for IIS setup.
|
||||
Logger.LogInformation("Successfully finished IIS application directory setup.");
|
||||
|
|
@ -100,29 +111,254 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
applicationBaseUri: uri.ToString(),
|
||||
contentRoot: contentRoot,
|
||||
hostShutdownToken: _hostShutdownToken.Token,
|
||||
hostProcess: _application.HostProcess
|
||||
hostProcess: HostProcess
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetLogsFromFile(string file)
|
||||
private void GetLogsFromFile()
|
||||
{
|
||||
var arr = new string[0];
|
||||
|
||||
RetryHelper.RetryOperation(() => arr = File.ReadAllLines(Path.Combine(DeploymentParameters.PublishedApplicationRootPath, file)),
|
||||
(ex) => Logger.LogWarning("Could not read log file"),
|
||||
RetryHelper.RetryOperation(() => arr = File.ReadAllLines(Path.Combine(DeploymentParameters.PublishedApplicationRootPath, _debugLogFile)),
|
||||
(ex) => Logger.LogWarning(ex, "Could not read log file"),
|
||||
5,
|
||||
200);
|
||||
|
||||
if (arr.Length == 0)
|
||||
{
|
||||
Logger.LogWarning($"{file} is empty.");
|
||||
}
|
||||
|
||||
foreach (var line in arr)
|
||||
{
|
||||
Logger.LogInformation(line);
|
||||
}
|
||||
|
||||
if (File.Exists(_debugLogFile))
|
||||
{
|
||||
File.Delete(_debugLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartIIS(Uri uri, string contentRoot)
|
||||
{
|
||||
// Backup currently deployed apphost.config file
|
||||
using (Logger.BeginScope("StartIIS"))
|
||||
{
|
||||
var port = uri.Port;
|
||||
if (port == 0)
|
||||
{
|
||||
throw new NotSupportedException("Cannot set port 0 for IIS.");
|
||||
}
|
||||
|
||||
AddTemporaryAppHostConfig(contentRoot, port);
|
||||
|
||||
await WaitUntilSiteStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitUntilSiteStarted()
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
while (sw.Elapsed < _timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var serverManager = new ServerManager())
|
||||
{
|
||||
var site = serverManager.Sites.Single();
|
||||
var appPool = serverManager.ApplicationPools.Single();
|
||||
|
||||
if (site.State == ObjectState.Started)
|
||||
{
|
||||
var workerProcess = appPool.WorkerProcesses.SingleOrDefault();
|
||||
if (workerProcess != null)
|
||||
{
|
||||
HostProcess = Process.GetProcessById(workerProcess.ProcessId);
|
||||
Logger.LogInformation("Site has started.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (appPool.State != ObjectState.Started && appPool.State != ObjectState.Starting)
|
||||
{
|
||||
var state = appPool.Start();
|
||||
Logger.LogInformation($"Starting pool, state: {state.ToString()}");
|
||||
}
|
||||
if (site.State != ObjectState.Starting)
|
||||
{
|
||||
var state = site.Start();
|
||||
Logger.LogInformation($"Starting site, state: {state.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex) when (IsExpectedException(ex))
|
||||
{
|
||||
// Accessing the site.State property while the site
|
||||
// is starting up returns the COMException
|
||||
// The object identifier does not represent a valid object.
|
||||
// (Exception from HRESULT: 0x800710D8)
|
||||
// This also means the site is not started yet, so catch and retry
|
||||
// after waiting.
|
||||
}
|
||||
|
||||
await Task.Delay(_retryDelay);
|
||||
}
|
||||
|
||||
throw new TimeoutException($"IIS failed to start site.");
|
||||
}
|
||||
|
||||
public async Task StopAndDeleteAppPool()
|
||||
{
|
||||
Stop();
|
||||
|
||||
await WaitUntilSiteStopped();
|
||||
|
||||
RestoreAppHostConfig();
|
||||
}
|
||||
|
||||
private async Task WaitUntilSiteStopped()
|
||||
{
|
||||
using (var serverManager = new ServerManager())
|
||||
{
|
||||
var site = serverManager.Sites.SingleOrDefault();
|
||||
if (site == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
while (sw.Elapsed < _timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (site.State == ObjectState.Stopped)
|
||||
{
|
||||
if (HostProcess.HasExited)
|
||||
{
|
||||
Logger.LogInformation($"Site has stopped successfully.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (IsExpectedException(ex))
|
||||
{
|
||||
// Accessing the site.State property while the site
|
||||
// is shutdown down returns the COMException
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogWarning($"IIS has not stopped after {sw.Elapsed.TotalMilliseconds}");
|
||||
await Task.Delay(_retryDelay);
|
||||
}
|
||||
|
||||
throw new TimeoutException($"IIS failed to stop site {site}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsExpectedException(Exception ex)
|
||||
{
|
||||
return ex is DllNotFoundException ||
|
||||
ex is COMException &&
|
||||
(ex.HResult == ERROR_OBJECT_NOT_FOUND || ex.HResult == ERROR_SHARING_VIOLATION || ex.HResult == ERROR_SERVICE_CANNOT_ACCEPT_CTRL);
|
||||
}
|
||||
|
||||
private void AddTemporaryAppHostConfig(string contentRoot, int port)
|
||||
{
|
||||
_configPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("D"));
|
||||
var appHostConfigPath = Path.Combine(_configPath, "applicationHost.config");
|
||||
Directory.CreateDirectory(_configPath);
|
||||
var config = XDocument.Parse(DeploymentParameters.ServerConfigTemplateContent ?? File.ReadAllText("IIS.config"));
|
||||
|
||||
ConfigureAppHostConfig(config.Root, contentRoot, port);
|
||||
|
||||
config.Save(appHostConfigPath);
|
||||
|
||||
using (var serverManager = new ServerManager())
|
||||
{
|
||||
var redirectionConfiguration = serverManager.GetRedirectionConfiguration();
|
||||
var redirectionSection = redirectionConfiguration.GetSection("configurationRedirection");
|
||||
|
||||
redirectionSection.Attributes["enabled"].Value = true;
|
||||
redirectionSection.Attributes["path"].Value = _configPath;
|
||||
|
||||
serverManager.CommitChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreAppHostConfig()
|
||||
{
|
||||
using (var serverManager = new ServerManager())
|
||||
{
|
||||
var redirectionConfiguration = serverManager.GetRedirectionConfiguration();
|
||||
var redirectionSection = redirectionConfiguration.GetSection("configurationRedirection");
|
||||
|
||||
redirectionSection.Attributes["enabled"].Value = false;
|
||||
|
||||
serverManager.CommitChanges();
|
||||
|
||||
Directory.Delete(_configPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureAppHostConfig(XElement config, string contentRoot, int port)
|
||||
{
|
||||
var siteElement = config
|
||||
.RequiredElement("system.applicationHost")
|
||||
.RequiredElement("sites")
|
||||
.RequiredElement("site");
|
||||
|
||||
siteElement
|
||||
.RequiredElement("application")
|
||||
.RequiredElement("virtualDirectory")
|
||||
.SetAttributeValue("physicalPath", contentRoot);
|
||||
|
||||
siteElement
|
||||
.RequiredElement("bindings")
|
||||
.RequiredElement("binding")
|
||||
.SetAttributeValue("bindingInformation", $"*:{port}:");
|
||||
|
||||
var ancmVersion = DeploymentParameters.AncmVersion.ToString();
|
||||
config
|
||||
.RequiredElement("system.webServer")
|
||||
.RequiredElement("globalModules")
|
||||
.GetOrAdd("add", "name", ancmVersion)
|
||||
.SetAttributeValue("image", GetAncmLocation(DeploymentParameters.AncmVersion));
|
||||
|
||||
config
|
||||
.RequiredElement("system.webServer")
|
||||
.RequiredElement("modules")
|
||||
.GetOrAdd("add", "name", ancmVersion);
|
||||
|
||||
var pool = config
|
||||
.RequiredElement("system.applicationHost")
|
||||
.RequiredElement("applicationPools")
|
||||
.RequiredElement("add");
|
||||
|
||||
var environmentVariables = pool
|
||||
.GetOrAdd("environmentVariables");
|
||||
|
||||
foreach (var tuple in DeploymentParameters.EnvironmentVariables)
|
||||
{
|
||||
environmentVariables
|
||||
.GetOrAdd("add", "name", tuple.Key)
|
||||
.SetAttributeValue("value", tuple.Value);
|
||||
}
|
||||
|
||||
RunServerConfigActions(config, contentRoot);
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
using (var serverManager = new ServerManager())
|
||||
{
|
||||
var site = serverManager.Sites.SingleOrDefault();
|
||||
site.Stop();
|
||||
var appPool = serverManager.ApplicationPools.SingleOrDefault();
|
||||
appPool.Stop();
|
||||
serverManager.CommitChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
protected List<Action<XElement, string>> DefaultWebConfigActions { get; } = new List<Action<XElement, string>>();
|
||||
|
||||
protected List<Action<XElement, string>> DefaultServerConfigActions { get; } = new List<Action<XElement, string>>();
|
||||
|
||||
public IISDeployerBase(IISDeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
|
||||
: base(deploymentParameters, loggerFactory)
|
||||
{
|
||||
|
|
@ -26,11 +28,6 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
public void RunWebConfigActions(string contentRoot)
|
||||
{
|
||||
if (IISDeploymentParameters == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DeploymentParameters.PublishApplicationBeforeDeployment)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot modify web.config file if no published output.");
|
||||
|
|
@ -38,37 +35,55 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
var path = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "web.config");
|
||||
var webconfig = XDocument.Load(path);
|
||||
var xElement = webconfig.Descendants("system.webServer").Single();
|
||||
|
||||
foreach (var action in DefaultWebConfigActions)
|
||||
{
|
||||
action.Invoke(xElement, contentRoot);
|
||||
action.Invoke(webconfig.Root, contentRoot);
|
||||
}
|
||||
|
||||
foreach (var action in IISDeploymentParameters.WebConfigActionList)
|
||||
if (IISDeploymentParameters != null)
|
||||
{
|
||||
action.Invoke(xElement, contentRoot);
|
||||
foreach (var action in IISDeploymentParameters.WebConfigActionList)
|
||||
{
|
||||
action.Invoke(webconfig.Root, contentRoot);
|
||||
}
|
||||
}
|
||||
|
||||
webconfig.Save(path);
|
||||
}
|
||||
|
||||
|
||||
public string RunServerConfigActions(string serverConfigString, string contentRoot)
|
||||
public void RunServerConfigActions(XElement config, string contentRoot)
|
||||
{
|
||||
if (IISDeploymentParameters == null)
|
||||
foreach (var action in DefaultServerConfigActions)
|
||||
{
|
||||
return serverConfigString;
|
||||
action.Invoke(config, contentRoot);
|
||||
}
|
||||
|
||||
var serverConfig = XDocument.Parse(serverConfigString);
|
||||
var xElement = serverConfig.Descendants("configuration").FirstOrDefault();
|
||||
|
||||
foreach (var action in IISDeploymentParameters.ServerConfigActionList)
|
||||
if (IISDeploymentParameters != null)
|
||||
{
|
||||
action.Invoke(xElement, contentRoot);
|
||||
foreach (var action in IISDeploymentParameters.ServerConfigActionList)
|
||||
{
|
||||
action.Invoke(config, contentRoot);
|
||||
}
|
||||
}
|
||||
return xElement.ToString();
|
||||
}
|
||||
|
||||
protected string GetAncmLocation(AncmVersion version)
|
||||
{
|
||||
var ancmDllName = version == AncmVersion.AspNetCoreModuleV2 ? "aspnetcorev2.dll" : "aspnetcore.dll";
|
||||
var arch = DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x64 ? $@"x64\{ancmDllName}" : $@"x86\{ancmDllName}";
|
||||
var ancmFile = Path.Combine(AppContext.BaseDirectory, arch);
|
||||
if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile)))
|
||||
{
|
||||
ancmFile = Path.Combine(AppContext.BaseDirectory, ancmDllName);
|
||||
if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile)))
|
||||
{
|
||||
throw new FileNotFoundException("AspNetCoreModule could not be found.", ancmFile);
|
||||
}
|
||||
}
|
||||
|
||||
return ancmFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||
{
|
||||
|
|
@ -65,63 +63,47 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
private Action<XElement, string> AddWebConfigEnvironmentVariables()
|
||||
{
|
||||
return (xElement, _) =>
|
||||
return (element, _) =>
|
||||
{
|
||||
if (WebConfigBasedEnvironmentVariables.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var element = xElement.Descendants("environmentVariables").SingleOrDefault();
|
||||
if (element == null)
|
||||
{
|
||||
element = new XElement("environmentVariables");
|
||||
xElement.Descendants("aspNetCore").SingleOrDefault().Add(element);
|
||||
}
|
||||
var environmentVariables = element
|
||||
.RequiredElement("system.webServer")
|
||||
.RequiredElement("aspNetCore")
|
||||
.GetOrAdd("environmentVariables");
|
||||
|
||||
|
||||
foreach (var envVar in WebConfigBasedEnvironmentVariables)
|
||||
{
|
||||
CreateOrSetElement(element, envVar.Key, envVar.Value, "environmentVariable");
|
||||
environmentVariables.GetOrAdd("environmentVariable", "name", envVar.Key)
|
||||
.SetAttributeValue("value", envVar.Value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Action<XElement, string> AddHandlerSettings()
|
||||
{
|
||||
return (xElement, _) =>
|
||||
return (element, _) =>
|
||||
{
|
||||
if (HandlerSettings.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var element = xElement.Descendants("handlerSettings").SingleOrDefault();
|
||||
if (element == null)
|
||||
{
|
||||
element = new XElement("handlerSettings");
|
||||
xElement.Descendants("aspNetCore").SingleOrDefault().Add(element);
|
||||
}
|
||||
var handlerSettings = element
|
||||
.RequiredElement("system.webServer")
|
||||
.RequiredElement("aspNetCore")
|
||||
.GetOrAdd("handlerSettings");
|
||||
|
||||
foreach (var handlerSetting in HandlerSettings)
|
||||
{
|
||||
CreateOrSetElement(element, handlerSetting.Key, handlerSetting.Value, "handlerSetting");
|
||||
handlerSettings.GetOrAdd("handlerSetting", "name", handlerSetting.Key)
|
||||
.SetAttributeValue("value", handlerSetting.Value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void CreateOrSetElement(XElement rootElement, string name, string value, string elementName)
|
||||
{
|
||||
if (rootElement.Descendants()
|
||||
.Attributes()
|
||||
.Where(attribute => attribute.Value == name)
|
||||
.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var element = new XElement(elementName);
|
||||
element.SetAttributeValue("name", name);
|
||||
element.SetAttributeValue("value", value);
|
||||
rootElement.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,8 +273,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
// Pass on the applicationhost.config to iis express. With this don't need to pass in the /path /port switches as they are in the applicationHost.config
|
||||
// We take a copy of the original specified applicationHost.Config to prevent modifying the one in the repo.
|
||||
serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMPath]", dllName: "aspnetcore.dll", serverConfig);
|
||||
serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMV2Path]", dllName: "aspnetcorev2.dll", serverConfig);
|
||||
serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMPath]", AncmVersion.AspNetCoreModule, serverConfig);
|
||||
serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMV2Path]", AncmVersion.AspNetCoreModuleV2, serverConfig);
|
||||
|
||||
serverConfig = ReplacePlaceholder(serverConfig, "[PORT]", port.ToString(CultureInfo.InvariantCulture));
|
||||
serverConfig = ReplacePlaceholder(serverConfig, "[ApplicationPhysicalPath]", contentRoot);
|
||||
|
|
@ -299,7 +299,10 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
serverConfig = ReplacePlaceholder(serverConfig, "[HostingModel]", DeploymentParameters.HostingModel.ToString());
|
||||
serverConfig = ReplacePlaceholder(serverConfig, "[AspNetCoreModule]", DeploymentParameters.AncmVersion.ToString());
|
||||
}
|
||||
serverConfig = RunServerConfigActions(serverConfig, contentRoot);
|
||||
|
||||
var config = XDocument.Parse(serverConfig);
|
||||
RunServerConfigActions(config.Root, contentRoot);
|
||||
serverConfig = config.ToString();
|
||||
|
||||
DeploymentParameters.ServerConfigLocation = Path.GetTempFileName();
|
||||
Logger.LogDebug("Saving Config to {configPath}", DeploymentParameters.ServerConfigLocation);
|
||||
|
|
@ -317,21 +320,11 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
return content;
|
||||
}
|
||||
|
||||
private string ModifyANCMPathInConfig(string replaceFlag, string dllName, string serverConfig)
|
||||
private string ModifyANCMPathInConfig(string replaceFlag, AncmVersion version, string serverConfig)
|
||||
{
|
||||
var dllRoot = AppContext.BaseDirectory;
|
||||
if (serverConfig.Contains(replaceFlag))
|
||||
{
|
||||
var arch = DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x64 ? $@"x64\{dllName}" : $@"x86\{dllName}";
|
||||
var ancmFile = Path.Combine(dllRoot, arch);
|
||||
if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile)))
|
||||
{
|
||||
ancmFile = Path.Combine(dllRoot, dllName);
|
||||
if (!File.Exists(Environment.ExpandEnvironmentVariables(ancmFile)))
|
||||
{
|
||||
throw new FileNotFoundException("AspNetCoreModule could not be found.", ancmFile);
|
||||
}
|
||||
}
|
||||
var ancmFile = GetAncmLocation(version);
|
||||
|
||||
Logger.LogDebug($"Writing '{replaceFlag}' '{ancmFile}' to config");
|
||||
return serverConfig.Replace(replaceFlag, ancmFile);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||
|
|
@ -10,13 +9,12 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
public static class WebConfigHelpers
|
||||
{
|
||||
public static Action<XElement, string> AddOrModifyAspNetCoreSection(string key, string value)
|
||||
=> AddAction(key, value, section: "aspNetCore");
|
||||
|
||||
public static Action<XElement, string> AddAction(string key, string value, string section)
|
||||
{
|
||||
return (element, _) =>
|
||||
{
|
||||
element.Descendants(section).SingleOrDefault().SetAttributeValue(key, value);
|
||||
return (element, _) => {
|
||||
element
|
||||
.GetOrAdd("system.webServer")
|
||||
.GetOrAdd("aspNetCore")
|
||||
.SetAttributeValue(key, value);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -24,11 +22,11 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
{
|
||||
return (element, _) =>
|
||||
{
|
||||
element.Descendants("handlers")
|
||||
.FirstOrDefault()
|
||||
.Descendants("add")
|
||||
.FirstOrDefault()
|
||||
.SetAttributeValue(key, value);
|
||||
element
|
||||
.GetOrAdd("system.webServer")
|
||||
.GetOrAdd("handlers")
|
||||
.GetOrAdd("add")
|
||||
.SetAttributeValue(key, value);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||
{
|
||||
internal static class XElementExtensions
|
||||
{
|
||||
public static XElement RequiredElement(this XElement element, string name)
|
||||
{
|
||||
var existing = element.Element(name);
|
||||
if (existing == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Element with name {name} not found in {element}");
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public static XElement GetOrAdd(this XElement element, string name)
|
||||
{
|
||||
var existing = element.Element(name);
|
||||
if (existing == null)
|
||||
{
|
||||
existing = new XElement(name);
|
||||
element.Add(existing);
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public static XElement GetOrAdd(this XElement element, string name, string attribute, string attributeValue)
|
||||
{
|
||||
var existing = element.Elements(name).FirstOrDefault(e => e.Attribute(attribute)?.Value == attributeValue);
|
||||
if (existing == null)
|
||||
{
|
||||
existing = new XElement(name, new XAttribute(attribute, attributeValue));
|
||||
element.Add(existing);
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
For schema documentation, see
|
||||
%windir%\system32\inetsrv\config\schema\IIS_schema.xml.
|
||||
|
||||
|
||||
Please make a backup of this file before making any changes to it.
|
||||
|
||||
-->
|
||||
|
|
@ -17,20 +17,20 @@
|
|||
The <configSections> section controls the registration of sections.
|
||||
Section is the basic unit of deployment, locking, searching and
|
||||
containment for configuration settings.
|
||||
|
||||
|
||||
Every section belongs to one section group.
|
||||
A section group is a container of logically-related sections.
|
||||
|
||||
|
||||
Sections cannot be nested.
|
||||
Section groups may be nested.
|
||||
|
||||
|
||||
<section
|
||||
name="" [Required, Collection Key] [XML name of the section]
|
||||
allowDefinition="Everywhere" [MachineOnly|MachineToApplication|AppHostOnly|Everywhere] [Level where it can be set]
|
||||
overrideModeDefault="Allow" [Allow|Deny] [Default delegation mode]
|
||||
allowLocation="true" [true|false] [Allowed in location tags]
|
||||
/>
|
||||
|
||||
|
||||
The recommended way to unlock sections is by using a location tag:
|
||||
<location path="Default Web Site" overrideMode="Allow">
|
||||
<system.webServer>
|
||||
|
|
@ -131,7 +131,9 @@
|
|||
<system.applicationHost>
|
||||
|
||||
<applicationPools>
|
||||
<add name="DefaultAppPool" autoStart="true" />
|
||||
<add name="DefaultAppPool" autoStart="true" startMode="AlwaysRunning">
|
||||
<processModel identityType="LocalSystem" />
|
||||
</add>
|
||||
<applicationPoolDefaults managedRuntimeVersion="v4.0">
|
||||
<processModel identityType="ApplicationPoolIdentity" loadUserProfile="true" setProfileEnvironment="false" />
|
||||
</applicationPoolDefaults>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace FileOutManagerStartupTests
|
|||
wprintf(expected, out);
|
||||
}
|
||||
|
||||
for (auto & p : std::experimental::filesystem::directory_iterator(tempDirectory.path()))
|
||||
for (auto & p : std::filesystem::directory_iterator(tempDirectory.path()))
|
||||
{
|
||||
std::wstring filename(p.path().filename());
|
||||
ASSERT_EQ(filename.substr(0, fileNamePrefix.size()), fileNamePrefix);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
namespace GlobalVersionTests
|
||||
{
|
||||
using ::testing::Test;
|
||||
namespace fs = std::experimental::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class GlobalVersionTest : public Test
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ TempDirectory::TempDirectory()
|
|||
RPC_CSTR szUuid = NULL;
|
||||
if (UuidToStringA(&uuid, &szUuid) == RPC_S_OK)
|
||||
{
|
||||
m_path = std::experimental::filesystem::temp_directory_path() / szUuid;
|
||||
m_path = std::filesystem::temp_directory_path() / reinterpret_cast<PCHAR>(szUuid);
|
||||
RpcStringFreeA(&szUuid);
|
||||
return;
|
||||
}
|
||||
|
|
@ -37,5 +37,5 @@ TempDirectory::TempDirectory()
|
|||
|
||||
TempDirectory::~TempDirectory()
|
||||
{
|
||||
std::experimental::filesystem::remove_all(m_path);
|
||||
std::filesystem::remove_all(m_path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ public:
|
|||
|
||||
~TempDirectory();
|
||||
|
||||
std::experimental::filesystem::path path() const
|
||||
std::filesystem::path path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
std::experimental::filesystem::path m_path;
|
||||
std::filesystem::path m_path;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue