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:
Pavel Krymets 2018-08-08 12:47:30 -07:00 committed by GitHub
commit 11514cd17f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 417 additions and 524 deletions

View File

@ -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

View File

@ -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

View File

@ -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";
}

View File

@ -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;

View File

@ -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("");

View File

@ -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
);
};

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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);
};
}
}

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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);

View File

@ -7,7 +7,7 @@
namespace GlobalVersionTests
{
using ::testing::Test;
namespace fs = std::experimental::filesystem;
namespace fs = std::filesystem;
class GlobalVersionTest : public Test
{

View File

@ -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);
}

View File

@ -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;
};