Add Nginx Deployer
This commit is contained in:
parent
b06a84457d
commit
22eab75005
|
|
@ -61,9 +61,9 @@ namespace Microsoft.AspNetCore.Server.Testing
|
||||||
|
|
||||||
public string EnvironmentName { get; set; }
|
public string EnvironmentName { get; set; }
|
||||||
|
|
||||||
public string ApplicationHostConfigTemplateContent { get; set; }
|
public string ServerConfigTemplateContent { get; set; }
|
||||||
|
|
||||||
public string ApplicationHostConfigLocation { get; set; }
|
public string ServerConfigLocation { get; set; }
|
||||||
|
|
||||||
public string SiteName { get; set; }
|
public string SiteName { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Server.Testing
|
||||||
IISExpress,
|
IISExpress,
|
||||||
IIS,
|
IIS,
|
||||||
WebListener,
|
WebListener,
|
||||||
Kestrel
|
Kestrel,
|
||||||
|
Nginx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +40,8 @@ namespace Microsoft.AspNetCore.Server.Testing
|
||||||
case ServerType.WebListener:
|
case ServerType.WebListener:
|
||||||
case ServerType.Kestrel:
|
case ServerType.Kestrel:
|
||||||
return new SelfHostDeployer(deploymentParameters, logger);
|
return new SelfHostDeployer(deploymentParameters, logger);
|
||||||
|
case ServerType.Nginx:
|
||||||
|
return new NginxDeployer(deploymentParameters, logger);
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException(
|
throw new NotSupportedException(
|
||||||
string.Format("Found no deployers suitable for server type '{0}' with the current runtime.",
|
string.Format("Found no deployers suitable for server type '{0}' with the current runtime.",
|
||||||
|
|
|
||||||
|
|
@ -50,19 +50,19 @@ namespace Microsoft.AspNetCore.Server.Testing
|
||||||
|
|
||||||
private CancellationToken StartIISExpress(Uri uri)
|
private CancellationToken StartIISExpress(Uri uri)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(DeploymentParameters.ApplicationHostConfigTemplateContent))
|
if (!string.IsNullOrWhiteSpace(DeploymentParameters.ServerConfigTemplateContent))
|
||||||
{
|
{
|
||||||
// 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
|
// 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.
|
// We take a copy of the original specified applicationHost.Config to prevent modifying the one in the repo.
|
||||||
|
|
||||||
DeploymentParameters.ApplicationHostConfigTemplateContent =
|
DeploymentParameters.ServerConfigTemplateContent =
|
||||||
DeploymentParameters.ApplicationHostConfigTemplateContent
|
DeploymentParameters.ServerConfigTemplateContent
|
||||||
.Replace("[ApplicationPhysicalPath]", DeploymentParameters.ApplicationPath)
|
.Replace("[ApplicationPhysicalPath]", DeploymentParameters.ApplicationPath)
|
||||||
.Replace("[PORT]", uri.Port.ToString());
|
.Replace("[PORT]", uri.Port.ToString());
|
||||||
|
|
||||||
DeploymentParameters.ApplicationHostConfigLocation = Path.GetTempFileName();
|
DeploymentParameters.ServerConfigLocation = Path.GetTempFileName();
|
||||||
|
|
||||||
File.WriteAllText(DeploymentParameters.ApplicationHostConfigLocation, DeploymentParameters.ApplicationHostConfigTemplateContent);
|
File.WriteAllText(DeploymentParameters.ServerConfigLocation, DeploymentParameters.ServerConfigTemplateContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
var webroot = DeploymentParameters.ApplicationPath;
|
var webroot = DeploymentParameters.ApplicationPath;
|
||||||
|
|
@ -71,9 +71,9 @@ namespace Microsoft.AspNetCore.Server.Testing
|
||||||
webroot = Path.Combine(webroot, "wwwroot");
|
webroot = Path.Combine(webroot, "wwwroot");
|
||||||
}
|
}
|
||||||
|
|
||||||
var parameters = string.IsNullOrWhiteSpace(DeploymentParameters.ApplicationHostConfigLocation) ?
|
var parameters = string.IsNullOrWhiteSpace(DeploymentParameters.ServerConfigLocation) ?
|
||||||
string.Format("/port:{0} /path:\"{1}\" /trace:error", uri.Port, webroot) :
|
string.Format("/port:{0} /path:\"{1}\" /trace:error", uri.Port, webroot) :
|
||||||
string.Format("/site:{0} /config:{1} /trace:error", DeploymentParameters.SiteName, DeploymentParameters.ApplicationHostConfigLocation);
|
string.Format("/site:{0} /config:{1} /trace:error", DeploymentParameters.SiteName, DeploymentParameters.ServerConfigLocation);
|
||||||
|
|
||||||
var iisExpressPath = GetIISExpressPath();
|
var iisExpressPath = GetIISExpressPath();
|
||||||
|
|
||||||
|
|
@ -140,18 +140,18 @@ namespace Microsoft.AspNetCore.Server.Testing
|
||||||
{
|
{
|
||||||
ShutDownIfAnyHostProcess(_hostProcess);
|
ShutDownIfAnyHostProcess(_hostProcess);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(DeploymentParameters.ApplicationHostConfigLocation)
|
if (!string.IsNullOrWhiteSpace(DeploymentParameters.ServerConfigLocation)
|
||||||
&& File.Exists(DeploymentParameters.ApplicationHostConfigLocation))
|
&& File.Exists(DeploymentParameters.ServerConfigLocation))
|
||||||
{
|
{
|
||||||
// Delete the temp applicationHostConfig that we created.
|
// Delete the temp applicationHostConfig that we created.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Delete(DeploymentParameters.ApplicationHostConfigLocation);
|
File.Delete(DeploymentParameters.ServerConfigLocation);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
// Ignore delete failures - just write a log.
|
// Ignore delete failures - just write a log.
|
||||||
Logger.LogWarning("Failed to delete '{config}'. Exception : {exception}", DeploymentParameters.ApplicationHostConfigLocation, exception.Message);
|
Logger.LogWarning("Failed to delete '{config}'. Exception : {exception}", DeploymentParameters.ServerConfigLocation, exception.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
// 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.Net.Http;
|
||||||
|
using Microsoft.AspNetCore.Server.Testing.Common;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Testing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Deployer for Kestrel on Nginx.
|
||||||
|
/// </summary>
|
||||||
|
public class NginxDeployer : SelfHostDeployer
|
||||||
|
{
|
||||||
|
private string _configFile;
|
||||||
|
private readonly int _waitTime = (int)TimeSpan.FromSeconds(30).TotalMilliseconds;
|
||||||
|
|
||||||
|
public NginxDeployer(DeploymentParameters deploymentParameters, ILogger logger)
|
||||||
|
: base(deploymentParameters, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DeploymentResult Deploy()
|
||||||
|
{
|
||||||
|
_configFile = Path.GetTempFileName();
|
||||||
|
var uri = new Uri(DeploymentParameters.ApplicationBaseUriHint);
|
||||||
|
|
||||||
|
var redirectUri = $"http://localhost:{TestUriHelper.FindFreePort()}";
|
||||||
|
|
||||||
|
var exitToken = StartSelfHost(new Uri(redirectUri));
|
||||||
|
|
||||||
|
SetupNginx(redirectUri, uri);
|
||||||
|
|
||||||
|
// Wait for App to be loaded since Nginx returns 502 instead of 503 when App isn't loaded
|
||||||
|
// Target actual address to avoid going through Nginx proxy
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
var response = RetryHelper.RetryRequest(() =>
|
||||||
|
{
|
||||||
|
return httpClient.GetAsync(redirectUri);
|
||||||
|
}, Logger).Result;
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Deploy failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DeploymentResult
|
||||||
|
{
|
||||||
|
WebRootLocation = DeploymentParameters.ApplicationPath,
|
||||||
|
DeploymentParameters = DeploymentParameters,
|
||||||
|
ApplicationBaseUri = uri.ToString(),
|
||||||
|
HostShutdownToken = exitToken
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupNginx(string redirectUri, Uri originalUri)
|
||||||
|
{
|
||||||
|
// copy nginx.conf template and replace pertinent information
|
||||||
|
DeploymentParameters.ServerConfigTemplateContent = DeploymentParameters.ServerConfigTemplateContent
|
||||||
|
.Replace("[user]", Environment.GetEnvironmentVariable("LOGNAME"))
|
||||||
|
.Replace("[listenPort]", originalUri.Port.ToString())
|
||||||
|
.Replace("[redirectUri]", redirectUri)
|
||||||
|
.Replace("[pidFile]", Path.Combine(DeploymentParameters.ApplicationPath, Guid.NewGuid().ToString()));
|
||||||
|
File.WriteAllText(_configFile, DeploymentParameters.ServerConfigTemplateContent);
|
||||||
|
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "nginx",
|
||||||
|
Arguments = $"-c {_configFile}",
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
// Trying a work around for https://github.com/aspnet/Hosting/issues/140.
|
||||||
|
RedirectStandardInput = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var runNginx = new Process() { StartInfo = startInfo })
|
||||||
|
{
|
||||||
|
runNginx.ErrorDataReceived += (sender, dataArgs) =>
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(dataArgs.Data))
|
||||||
|
{
|
||||||
|
Logger.LogWarning("nginx: " + dataArgs.Data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
runNginx.OutputDataReceived += (sender, dataArgs) =>
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(dataArgs.Data))
|
||||||
|
{
|
||||||
|
Logger.LogInformation("nginx: " + dataArgs.Data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
runNginx.Start();
|
||||||
|
runNginx.BeginErrorReadLine();
|
||||||
|
runNginx.BeginOutputReadLine();
|
||||||
|
runNginx.WaitForExit(_waitTime);
|
||||||
|
if (runNginx.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to start Nginx");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_configFile))
|
||||||
|
{
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "nginx",
|
||||||
|
Arguments = $"-s stop -c {_configFile}",
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
// Trying a work around for https://github.com/aspnet/Hosting/issues/140.
|
||||||
|
RedirectStandardInput = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var runNginx = new Process() { StartInfo = startInfo })
|
||||||
|
{
|
||||||
|
runNginx.Start();
|
||||||
|
runNginx.WaitForExit(_waitTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Delete(_configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Testing
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private CancellationToken StartSelfHost(Uri uri)
|
protected CancellationToken StartSelfHost(Uri uri)
|
||||||
{
|
{
|
||||||
string executableName;
|
string executableName;
|
||||||
string executableArgs = string.Empty;
|
string executableArgs = string.Empty;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue