From ccde3309789a49bd2fb2b09c38ab90630a1f70c3 Mon Sep 17 00:00:00 2001 From: Troy Dai Date: Mon, 11 Jan 2016 00:58:40 -0800 Subject: [PATCH] Update IISDeployer and its base class --- .../Common/DeploymentParameters.cs | 8 +- .../Deployers/ApplicationDeployer.cs | 183 +++++++----------- .../Deployers/ApplicationDeployerFactory.cs | 4 +- .../Deployers/IISDeployer.cs | 132 ++++++------- .../Deployers/IISExpressDeployer.cs | 2 +- .../Deployers/SelfHostDeployer.cs | 6 +- 6 files changed, 135 insertions(+), 200 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Testing/Common/DeploymentParameters.cs b/src/Microsoft.AspNet.Server.Testing/Common/DeploymentParameters.cs index f4b64df53f..83dfa962e0 100644 --- a/src/Microsoft.AspNet.Server.Testing/Common/DeploymentParameters.cs +++ b/src/Microsoft.AspNet.Server.Testing/Common/DeploymentParameters.cs @@ -42,11 +42,11 @@ namespace Microsoft.AspNet.Server.Testing EnvironmentVariables.Add(new KeyValuePair("ASPNET_DETAILEDERRORS", "true")); } - public ServerType ServerType { get; private set; } + public ServerType ServerType { get; } - public RuntimeFlavor RuntimeFlavor { get; private set; } + public RuntimeFlavor RuntimeFlavor { get; } - public RuntimeArchitecture RuntimeArchitecture { get; private set; } + public RuntimeArchitecture RuntimeArchitecture { get; } /// /// Suggested base url for the deployed application. The final deployed url could be @@ -77,8 +77,6 @@ namespace Microsoft.AspNet.Server.Testing /// public bool PublishWithNoSource { get; set; } - public string DnxRuntime { get; set; } - /// /// Environment variables to be set before starting the host. /// Not applicable for IIS Scenarios. diff --git a/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployer.cs b/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployer.cs index f2a07b24fd..8901e08f0d 100644 --- a/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployer.cs +++ b/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployer.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Text; -using System.Text.RegularExpressions; using System.Threading; using Microsoft.AspNet.Testing; using Microsoft.Extensions.Logging; @@ -19,128 +17,78 @@ namespace Microsoft.AspNet.Server.Testing /// public abstract class ApplicationDeployer : IApplicationDeployer { - /// - /// Example: runtimes/dnx-coreclr-win-x64.1.0.0-rc1-15844/bin - /// - protected string ChosenRuntimePath { get; set; } + private readonly Stopwatch _stopwatch = new Stopwatch(); - /// - /// Examples: dnx-coreclr-win-x64.1.0.0-rc1-15844, dnx-mono.1.0.0-rc1-15844 - /// - protected string ChosenRuntimeName { get; set; } - - protected DeploymentParameters DeploymentParameters { get; private set; } - - protected ILogger Logger { get; private set; } - - protected Stopwatch StopWatch { get; private set; } = new Stopwatch(); - - protected string OSPrefix - { - get - { - if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Linux) - { - return "linux"; - } - else if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Darwin) - { - return "darwin"; - } - else if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Windows) - { - return "win"; - } - else - { - throw new InvalidOperationException("Unrecognized operating system"); - } - } - } - - protected string DnuCommandName - { - get - { - if (TestPlatformHelper.IsWindows) - { - return "dnu.cmd"; - } - else - { - return "dnu"; - } - } - } - - protected string DnxCommandName - { - get - { - if (TestPlatformHelper.IsWindows) - { - return "dnx.exe"; - } - else - { - return "dnx"; - } - } - } - - public abstract DeploymentResult Deploy(); - - public ApplicationDeployer( - DeploymentParameters deploymentParameters, - ILogger logger) + public ApplicationDeployer(DeploymentParameters deploymentParameters, ILogger logger) { DeploymentParameters = deploymentParameters; Logger = logger; + DnuCommandName = TestPlatformHelper.IsWindows ? "dnu.cmd" : "dnu"; + DnxCommandName = TestPlatformHelper.IsWindows ? "dnx.exe" : "dnx"; } - protected string PopulateChosenRuntimeInformation() + protected DeploymentParameters DeploymentParameters { get; } + + protected ILogger Logger { get; } + + protected string DnuCommandName { get; } + + protected string DnxCommandName { get; } + + protected string TargetRuntimeName { get; private set; } + + protected string TargetRuntimeBinPath { get; private set; } + + protected string ToolingRuntimeBinPath { get; private set; } + + public abstract DeploymentResult Deploy(); + + protected void PickRuntime() { - string currentRuntimeBinPath = PlatformServices.Default.Runtime.RuntimePath; + var currentRuntimeBinPath = PlatformServices.Default.Runtime.RuntimePath; Logger.LogInformation($"Current runtime path is : {currentRuntimeBinPath}"); - string targetRuntimeName; + var currentRuntimeFullName = new DirectoryInfo(currentRuntimeBinPath).Parent.Name; + var currentRuntimeVersionParts = currentRuntimeFullName.Split(new char[] { '.' }, 2); + if (currentRuntimeVersionParts.Length < 2) + { + throw new ArgumentNullException($"The current runtime bin path points to a runtime name doesn't indicate a version: {currentRuntimeBinPath}."); + } + var currentRuntimeVersion = currentRuntimeVersionParts[1]; + + var runtimeHome = new DirectoryInfo(currentRuntimeBinPath).Parent.Parent.FullName; + Logger.LogInformation($"Runtime home folder: {runtimeHome}"); + if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.Mono) { - targetRuntimeName = "dnx-mono"; + // TODO: review on mono + TargetRuntimeName = $"dnx-mono.{currentRuntimeVersion}"; + TargetRuntimeBinPath = Path.Combine(runtimeHome, TargetRuntimeName, "bin"); + ToolingRuntimeBinPath = TargetRuntimeBinPath; } else { - targetRuntimeName = new StringBuilder() - .Append("dnx") - .Append((DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr) ? "-coreclr" : "-clr") - .Append($"-{OSPrefix}") - .Append((DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86) ? "-x86" : "-x64") - .ToString(); + var flavor = DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr ? "coreclr" : "clr"; + var architecture = DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86 ? "x86" : "x64"; + + // tooling runtime will stick to coreclr so as to prevent long path issue during publishing + ToolingRuntimeBinPath = Path.Combine(runtimeHome, $"dnx-coreclr-{GetOSPrefix()}-{architecture}.{currentRuntimeVersion}", "bin"); + + TargetRuntimeName = $"dnx-{flavor}-{GetOSPrefix()}-{architecture}.{currentRuntimeVersion}"; + TargetRuntimeBinPath = Path.Combine(runtimeHome, TargetRuntimeName, "bin"); } - var targetRuntimeBinPath = Regex.Replace( - currentRuntimeBinPath, - "dnx-(mono|((clr|coreclr)-(win|linux|darwin)-(x86|x64)))", - targetRuntimeName, - RegexOptions.IgnoreCase); - - var targetRuntimeBinDir = new DirectoryInfo(targetRuntimeBinPath); - if (targetRuntimeBinDir == null || !targetRuntimeBinDir.Exists) + if (!Directory.Exists(ToolingRuntimeBinPath) || !Directory.Exists(TargetRuntimeBinPath)) { - throw new Exception($"Requested runtime at location '{targetRuntimeBinPath}' does not exist.Please make sure it is installed before running test."); + throw new Exception($"Requested runtime '{ToolingRuntimeBinPath}' or '{TargetRuntimeBinPath}; does not exist. Please make sure it is installed before running test."); } - ChosenRuntimePath = targetRuntimeBinDir.FullName; - ChosenRuntimeName = targetRuntimeBinDir.Parent.Name; - DeploymentParameters.DnxRuntime = ChosenRuntimeName; - - Logger.LogInformation($"Chosen runtime path is {ChosenRuntimePath}"); + Logger.LogInformation($"Pick target runtime {TargetRuntimeBinPath}"); + Logger.LogInformation($"Pick tooling runtime {ToolingRuntimeBinPath}"); // Work around win7 search path issues. - var newPath = ChosenRuntimePath + Path.PathSeparator + Environment.GetEnvironmentVariable("PATH"); + var newPath = TargetRuntimeBinPath + Path.PathSeparator + Environment.GetEnvironmentVariable("PATH"); DeploymentParameters.EnvironmentVariables.Add(new KeyValuePair("PATH", newPath)); - - return ChosenRuntimeName; } protected void DnuPublish(string publishRoot = null) @@ -150,9 +98,9 @@ namespace Microsoft.AspNet.Server.Testing var noSource = DeploymentParameters.PublishWithNoSource ? "--no-source" : string.Empty; var command = DeploymentParameters.Command ?? "web"; var parameters = $"publish {DeploymentParameters.ApplicationPath} -o {DeploymentParameters.PublishedApplicationRootPath}" - + $" --runtime {DeploymentParameters.DnxRuntime} {noSource} --iis-command {command}"; + + $" --runtime {TargetRuntimeName} {noSource} --iis-command {command}"; - var dnuPath = Path.Combine(ChosenRuntimePath, DnuCommandName); + var dnuPath = Path.Combine(ToolingRuntimeBinPath, DnuCommandName); Logger.LogInformation($"Executing command {dnuPath} {parameters}"); var startInfo = new ProcessStartInfo @@ -166,8 +114,8 @@ namespace Microsoft.AspNet.Server.Testing }; var hostProcess = new Process() { StartInfo = startInfo }; - hostProcess.ErrorDataReceived += (sender, dataArgs) => { Logger.LogError(dataArgs.Data ?? string.Empty); }; - hostProcess.OutputDataReceived += (sender, dataArgs) => { Logger.LogInformation(dataArgs.Data ?? string.Empty); }; + hostProcess.ErrorDataReceived += (sender, dataArgs) => { Logger.LogWarning(dataArgs.Data ?? string.Empty); }; + hostProcess.OutputDataReceived += (sender, dataArgs) => { Logger.LogTrace(dataArgs.Data ?? string.Empty); }; hostProcess.Start(); hostProcess.BeginErrorReadLine(); hostProcess.BeginOutputReadLine(); @@ -180,10 +128,9 @@ namespace Microsoft.AspNet.Server.Testing DeploymentParameters.ApplicationPath = (DeploymentParameters.ServerType == ServerType.IISExpress || - DeploymentParameters.ServerType == ServerType.IIS) ? + DeploymentParameters.ServerType == ServerType.IIS) ? Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "wwwroot") : - Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "approot", "src", - new DirectoryInfo(DeploymentParameters.ApplicationPath).Name); + Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "approot", "src", new DirectoryInfo(DeploymentParameters.ApplicationPath).Name); Logger.LogInformation($"dnu publish finished with exit code : {hostProcess.ExitCode}"); } @@ -299,15 +246,27 @@ namespace Microsoft.AspNet.Server.Testing protected void StartTimer() { Logger.LogInformation($"Deploying {DeploymentParameters.ToString()}"); - StopWatch.Start(); + _stopwatch.Start(); } protected void StopTimer() { - StopWatch.Stop(); - Logger.LogInformation("[Time]: Total time taken for this test variation '{t}' seconds", StopWatch.Elapsed.TotalSeconds); + _stopwatch.Stop(); + Logger.LogInformation("[Time]: Total time taken for this test variation '{t}' seconds", _stopwatch.Elapsed.TotalSeconds); } public abstract void Dispose(); + + protected static string GetOSPrefix() + { + switch (PlatformServices.Default.Runtime.OperatingSystemPlatform) + { + case Platform.Linux: return "linux"; + case Platform.Darwin: return "darwin"; + case Platform.Windows: return "win"; + default: + throw new InvalidOperationException("Unrecognized operating system"); + } + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployerFactory.cs b/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployerFactory.cs index 4e4ae9d1e0..0b5776b8b4 100644 --- a/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployerFactory.cs +++ b/src/Microsoft.AspNet.Server.Testing/Deployers/ApplicationDeployerFactory.cs @@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Server.Testing { case ServerType.IISExpress: return new IISExpressDeployer(deploymentParameters, logger); -#if NET451 +#if DNX451 case ServerType.IIS: return new IISDeployer(deploymentParameters, logger); #endif @@ -48,4 +48,4 @@ namespace Microsoft.AspNet.Server.Testing } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNet.Server.Testing/Deployers/IISDeployer.cs b/src/Microsoft.AspNet.Server.Testing/Deployers/IISDeployer.cs index 5403dbc1df..3ba827af7c 100644 --- a/src/Microsoft.AspNet.Server.Testing/Deployers/IISDeployer.cs +++ b/src/Microsoft.AspNet.Server.Testing/Deployers/IISDeployer.cs @@ -1,12 +1,13 @@ // 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. -#if NET451 +#if DNX451 + using System; -using System.IO; using System.Linq; +using System.Net; +using System.Net.Sockets; using System.Threading; -using System.Xml; using Microsoft.Extensions.Logging; using Microsoft.Web.Administration; @@ -36,10 +37,10 @@ namespace Microsoft.AspNet.Server.Testing _application = new IISApplication(DeploymentParameters, Logger); - DeploymentParameters.DnxRuntime = PopulateChosenRuntimeInformation(); + PickRuntime(); // Publish to IIS root\application folder. - DnuPublish(publishRoot: _application.WebSiteRootFolder); + DnuPublish(); // Drop a json file instead of setting environment variable. SetAspEnvironmentWithJson(); @@ -59,19 +60,11 @@ namespace Microsoft.AspNet.Server.Testing WebRootLocation = DeploymentParameters.ApplicationPath, DeploymentParameters = DeploymentParameters, // Accomodate the vdir name. - ApplicationBaseUri = new UriBuilder(Uri.UriSchemeHttp, "localhost", IISApplication.Port, _application.VirtualDirectoryName).Uri.AbsoluteUri + "/", + ApplicationBaseUri = new UriBuilder(Uri.UriSchemeHttp, "localhost", _application.Port).Uri.AbsoluteUri + "/", HostShutdownToken = _hostShutdownToken.Token }; } - private void SetAspEnvironmentWithJson() - { - // Drop a hosting.json with Hosting:Environment information. - Logger.LogInformation("Creating hosting.json file with Hosting:Environment."); - var jsonFile = Path.Combine(DeploymentParameters.ApplicationPath, "hosting.json"); - File.WriteAllText(jsonFile, string.Format("{ \"Hosting:Environment\":\"{0}\" }", DeploymentParameters.EnvironmentName)); - } - public override void Dispose() { if (_application != null) @@ -83,6 +76,8 @@ namespace Microsoft.AspNet.Server.Testing } TriggerHostShutdown(_hostShutdownToken); + + Thread.Sleep(TimeSpan.FromSeconds(3)); } CleanPublishedOutput(); @@ -91,94 +86,79 @@ namespace Microsoft.AspNet.Server.Testing StopTimer(); } + private void SetAspEnvironmentWithJson() + { + ////S Drop a hosting.json with Hosting:Environment information. + // Logger.LogInformation("Creating hosting.json file with Hosting:Environment."); + // var jsonFile = Path.Combine(DeploymentParameters.ApplicationPath, "hosting.json"); + // File.WriteAllText(jsonFile, string.Format("{ \"Hosting:Environment\":\"{0}\" }", DeploymentParameters.EnvironmentName)); + } + private class IISApplication { - private const string WebSiteName = "ASPNETTESTRUNS"; - private readonly ServerManager _serverManager = new ServerManager(); private readonly DeploymentParameters _deploymentParameters; private readonly ILogger _logger; - private ApplicationPool _applicationPool; - private Application _application; - private Site _website; - - public string VirtualDirectoryName { get; set; } - - // Always create website with the same port. - public const int Port = 5100; - - public string WebSiteRootFolder - { - get - { - return Path.Combine( - Environment.GetEnvironmentVariable("SystemDrive") + @"\", - "inetpub", - WebSiteName); - } - } public IISApplication(DeploymentParameters deploymentParameters, ILogger logger) { _deploymentParameters = deploymentParameters; _logger = logger; + + WebSiteName = CreateTestSiteName(); + Port = FindFreePort(); } + public int Port { get; } + + public string WebSiteName { get; } + + public string WebSiteRootFolder => $"{Environment.GetEnvironmentVariable("SystemDrive")}\\inetpub\\{WebSiteName}"; + public void Deploy() { - VirtualDirectoryName = new DirectoryInfo(_deploymentParameters.ApplicationPath).Parent.Name; - _applicationPool = CreateAppPool(VirtualDirectoryName); - _application = Website.Applications.Add("/" + VirtualDirectoryName, _deploymentParameters.ApplicationPath); - _application.ApplicationPoolName = _applicationPool.Name; + _serverManager.Sites.Add(WebSiteName, _deploymentParameters.ApplicationPath, Port); _serverManager.CommitChanges(); } - private Site Website - { - get - { - _website = _serverManager.Sites.Where(s => s.Name == WebSiteName).FirstOrDefault(); - if (_website == null) - { - _website = _serverManager.Sites.Add(WebSiteName, WebSiteRootFolder, Port); - } - - return _website; - } - } - - private ApplicationPool CreateAppPool(string appPoolName) - { - var applicationPool = _serverManager.ApplicationPools.Add(appPoolName); - applicationPool.ManagedRuntimeVersion = string.Empty; - - applicationPool.Enable32BitAppOnWin64 = (_deploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86); - _logger.LogInformation("Created {bit} application pool '{name}' with runtime version {runtime}.", - _deploymentParameters.RuntimeArchitecture, applicationPool.Name, - string.IsNullOrEmpty(applicationPool.ManagedRuntimeVersion) ? "that is default" : applicationPool.ManagedRuntimeVersion); - return applicationPool; - } - public void StopAndDeleteAppPool() { - _logger.LogInformation("Stopping application pool '{name}' and deleting application.", _applicationPool.Name); - - if (_applicationPool != null) + if (string.IsNullOrEmpty(WebSiteName)) { - _applicationPool.Stop(); + return; } - // Remove the application from website. - if (_application != null) + var siteToRemove = _serverManager.Sites.FirstOrDefault(site => site.Name == WebSiteName); + if (siteToRemove != null) { - _application = Website.Applications.Where(a => a.Path == _application.Path).FirstOrDefault(); - Website.Applications.Remove(_application); - _serverManager.ApplicationPools.Remove(_serverManager.ApplicationPools[_applicationPool.Name]); + siteToRemove.Stop(); + _serverManager.Sites.Remove(siteToRemove); _serverManager.CommitChanges(); - _logger.LogInformation("Successfully stopped application pool '{name}' and deleted application from IIS.", _applicationPool.Name); + } + } + + private static int FindFreePort() + { + using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); + return ((IPEndPoint)socket.LocalEndPoint).Port; + } + } + + private string CreateTestSiteName() + { + if (!string.IsNullOrEmpty(_deploymentParameters.SiteName)) + { + return $"{_deploymentParameters.SiteName}{DateTime.Now.ToString("yyyyMMddHHmmss")}"; + } + else + { + return $"testsite{DateTime.Now.ToString("yyyyMMddHHmmss")}"; } } } } } -#endif \ No newline at end of file + +#endif diff --git a/src/Microsoft.AspNet.Server.Testing/Deployers/IISExpressDeployer.cs b/src/Microsoft.AspNet.Server.Testing/Deployers/IISExpressDeployer.cs index c60bf11b37..d3911ac925 100644 --- a/src/Microsoft.AspNet.Server.Testing/Deployers/IISExpressDeployer.cs +++ b/src/Microsoft.AspNet.Server.Testing/Deployers/IISExpressDeployer.cs @@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Server.Testing // Start timer StartTimer(); - DeploymentParameters.DnxRuntime = PopulateChosenRuntimeInformation(); + PickRuntime(); // For now we always auto-publish. Otherwise we'll have to write our own local web.config for the HttpPlatformHandler DeploymentParameters.PublishApplicationBeforeDeployment = true; diff --git a/src/Microsoft.AspNet.Server.Testing/Deployers/SelfHostDeployer.cs b/src/Microsoft.AspNet.Server.Testing/Deployers/SelfHostDeployer.cs index e1fdc509b0..c018f0dac7 100644 --- a/src/Microsoft.AspNet.Server.Testing/Deployers/SelfHostDeployer.cs +++ b/src/Microsoft.AspNet.Server.Testing/Deployers/SelfHostDeployer.cs @@ -2,11 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; -using Microsoft.AspNet.Testing; using Microsoft.Extensions.Logging; namespace Microsoft.AspNet.Server.Testing @@ -28,7 +26,7 @@ namespace Microsoft.AspNet.Server.Testing // Start timer StartTimer(); - DeploymentParameters.DnxRuntime = PopulateChosenRuntimeInformation(); + PickRuntime(); if (DeploymentParameters.PublishApplicationBeforeDeployment) { @@ -55,7 +53,7 @@ namespace Microsoft.AspNet.Server.Testing commandName = "run"; } - var dnxPath = Path.Combine(ChosenRuntimePath, DnxCommandName); + var dnxPath = Path.Combine(TargetRuntimeBinPath, DnxCommandName); var dnxArgs = $"-p \"{DeploymentParameters.ApplicationPath}\" {commandName} " + $"--server.urls {DeploymentParameters.ApplicationBaseUriHint} " + $"--server {(DeploymentParameters.ServerType == ServerType.WebListener ? "Microsoft.AspNet.Server.WebListener" : "Microsoft.AspNet.Server.Kestrel")}";