aspnetcore/src/Microsoft.AspNet.Server.Tes.../Deployers/ApplicationDeployer.cs

310 lines
11 KiB
C#

// 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.Linq;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.AspNet.Testing;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNet.Server.Testing
{
/// <summary>
/// Abstract base class of all deployers with implementation of some of the common helpers.
/// </summary>
public abstract class ApplicationDeployer : IApplicationDeployer
{
/// <summary>
/// Example: runtimes/dnx-coreclr-win-x64.1.0.0-rc1-15844/bin
/// </summary>
protected string ChosenRuntimePath { get; set; }
/// <summary>
/// Examples: dnx-coreclr-win-x64.1.0.0-rc1-15844, dnx-mono.1.0.0-rc1-15844
/// </summary>
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 (TestPlatformHelper.IsLinux)
{
return "linux";
}
else if (TestPlatformHelper.IsMac)
{
return "darwin";
}
else if (TestPlatformHelper.IsWindows)
{
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)
{
DeploymentParameters = deploymentParameters;
Logger = logger;
}
protected string PopulateChosenRuntimeInformation()
{
// ex: runtimes/dnx-coreclr-win-x64.1.0.0-rc1-15844/bin
var currentRuntimeBinPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
Logger.LogInformation($"Current runtime path is : {currentRuntimeBinPath}");
var targetRuntimeName = new StringBuilder()
.Append("dnx")
.Append((DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr) ? "-coreclr" : "-clr")
.Append($"-{OSPrefix}")
.Append((DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86) ? "-x86" : "-x64")
.ToString();
string targetRuntimeBinPath;
// Ex: When current runtime is Mono and the tests are being run for CoreClr
if (currentRuntimeBinPath.Contains("dnx-mono"))
{
targetRuntimeBinPath = currentRuntimeBinPath.Replace("dnx-mono", targetRuntimeName);
}
else
{
targetRuntimeBinPath = Regex.Replace(
currentRuntimeBinPath,
"dnx-(clr|coreclr)-(win|linux|darwin)-(x86|x64)",
targetRuntimeName,
RegexOptions.IgnoreCase);
}
var targetRuntimeBinDir = new DirectoryInfo(targetRuntimeBinPath);
if (targetRuntimeBinDir == null || !targetRuntimeBinDir.Exists)
{
throw new Exception($"Requested runtime at location '{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}");
return ChosenRuntimeName;
}
protected void DnuPublish(string publishRoot = null)
{
DeploymentParameters.PublishedApplicationRootPath = Path.Combine(publishRoot ?? Path.GetTempPath(), Guid.NewGuid().ToString());
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}";
var dnuPath = Path.Combine(ChosenRuntimePath, DnuCommandName);
Logger.LogInformation($"Executing command {dnuPath} {parameters}");
var startInfo = new ProcessStartInfo
{
FileName = dnuPath,
Arguments = parameters,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true
};
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.Start();
hostProcess.BeginErrorReadLine();
hostProcess.BeginOutputReadLine();
hostProcess.WaitForExit();
if (hostProcess.ExitCode != 0)
{
throw new Exception(string.Format("dnu publish exited with exit code : {0}", hostProcess.ExitCode));
}
DeploymentParameters.ApplicationPath =
(DeploymentParameters.ServerType == ServerType.IISExpress ||
DeploymentParameters.ServerType == ServerType.IIS) ?
Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "wwwroot") :
Path.Combine(DeploymentParameters.PublishedApplicationRootPath, "approot", "src",
new DirectoryInfo(DeploymentParameters.ApplicationPath).Name);
Logger.LogInformation($"dnu publish finished with exit code : {hostProcess.ExitCode}");
}
protected void CleanPublishedOutput()
{
try
{
// We've originally published the application in a temp folder. We need to delete it.
Directory.Delete(DeploymentParameters.PublishedApplicationRootPath, true);
}
catch (Exception exception)
{
Logger.LogWarning($"Failed to delete directory : {exception.Message}");
}
}
protected void ShutDownIfAnyHostProcess(Process hostProcess)
{
if (hostProcess != null && !hostProcess.HasExited)
{
// Shutdown the host process.
hostProcess.Kill();
hostProcess.WaitForExit(5 * 1000);
if (!hostProcess.HasExited)
{
Logger.LogWarning("Unable to terminate the host process with process Id '{processId}", hostProcess.Id);
}
else
{
Logger.LogInformation("Successfully terminated host process with process Id '{processId}'", hostProcess.Id);
}
}
else
{
Logger.LogWarning("Host process already exited or never started successfully.");
}
}
protected void AddEnvironmentVariablesToProcess(ProcessStartInfo startInfo)
{
var environment =
#if DNX451
startInfo.EnvironmentVariables;
#elif DNXCORE50
startInfo.Environment;
#endif
SetEnvironmentVariable(environment, "ASPNET_ENV", DeploymentParameters.EnvironmentName);
// Work around for https://github.com/aspnet/dnx/issues/1515
if (DeploymentParameters.PublishWithNoSource)
{
SetEnvironmentVariable(environment, "DNX_PACKAGES", null);
}
SetEnvironmentVariable(environment, "DNX_DEFAULT_LIB", null);
foreach (var environmentVariable in DeploymentParameters.EnvironmentVariables)
{
SetEnvironmentVariable(environment, environmentVariable.Key, environmentVariable.Value);
}
}
#if DNX451
protected void SetEnvironmentVariable(System.Collections.Specialized.StringDictionary environment, string name, string value)
{
#elif DNXCORE50
protected void SetEnvironmentVariable(System.Collections.Generic.IDictionary<string, string> environment, string name, string value)
{
#endif
if (value == null)
{
Logger.LogInformation("Removing environment variable {name}", name);
environment.Remove(name);
}
else
{
Logger.LogInformation("SET {name}={value}", name, value);
environment[name] = value;
}
}
protected void InvokeUserApplicationCleanup()
{
if (DeploymentParameters.UserAdditionalCleanup != null)
{
// User cleanup.
try
{
DeploymentParameters.UserAdditionalCleanup(DeploymentParameters);
}
catch (Exception exception)
{
Logger.LogWarning("User cleanup code failed with exception : {exception}", exception.Message);
}
}
}
protected void TriggerHostShutdown(CancellationTokenSource hostShutdownSource)
{
Logger.LogInformation("Host process shutting down.");
try
{
hostShutdownSource.Cancel();
}
catch (Exception)
{
// Suppress errors.
}
}
protected void StartTimer()
{
Logger.LogInformation($"Deploying {DeploymentParameters.ToString()}");
StopWatch.Start();
}
protected void StopTimer()
{
StopWatch.Stop();
Logger.LogInformation("[Time]: Total time taken for this test variation '{t}' seconds", StopWatch.Elapsed.TotalSeconds);
}
public abstract void Dispose();
}
}