public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("SelfHost.Deploy")) { // Start timer StartTimer(); if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } var hintUrl = TestUriHelper.BuildTestUri( DeploymentParameters.ServerType, DeploymentParameters.Scheme, DeploymentParameters.ApplicationBaseUriHint, DeploymentParameters.StatusMessagesEnabled); // Launch the host process. var(actualUrl, hostExitToken) = await StartSelfHostAsync(hintUrl); Logger.LogInformation("Application ready at URL: {appUrl}", actualUrl); return(new DeploymentResult( LoggerFactory, DeploymentParameters, applicationBaseUri: actualUrl.ToString(), contentRoot: DeploymentParameters.PublishApplicationBeforeDeployment ? DeploymentParameters.PublishedApplicationRootPath : DeploymentParameters.ApplicationPath, hostShutdownToken: hostExitToken)); } }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("Deployment")) { // Start timer StartTimer(); // For now we always auto-publish. Otherwise we'll have to write our own local web.config for the HttpPlatformHandler DeploymentParameters.PublishApplicationBeforeDeployment = true; if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } var contentRoot = DeploymentParameters.PublishApplicationBeforeDeployment ? DeploymentParameters.PublishedApplicationRootPath : DeploymentParameters.ApplicationPath; var testUri = TestUriHelper.BuildTestUri(DeploymentParameters.ApplicationBaseUriHint); // Launch the host process. var(actualUri, hostExitToken) = await StartIISExpressAsync(testUri, contentRoot); Logger.LogInformation("Application ready at URL: {appUrl}", actualUri); // Right now this works only for urls like http://localhost:5001/. Does not work for http://localhost:5001/subpath. return(new DeploymentResult( LoggerFactory, DeploymentParameters, applicationBaseUri: actualUri.ToString(), contentRoot: contentRoot, hostShutdownToken: hostExitToken)); } }
public override DeploymentResult Deploy() { // Start timer StartTimer(); // For now we always auto-publish. Otherwise we'll have to write our own local web.config for the HttpPlatformHandler DeploymentParameters.PublishApplicationBeforeDeployment = true; if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } var contentRoot = DeploymentParameters.PublishApplicationBeforeDeployment ? DeploymentParameters.PublishedApplicationRootPath : DeploymentParameters.ApplicationPath; var uri = TestUriHelper.BuildTestUri(DeploymentParameters.ApplicationBaseUriHint); // Launch the host process. var hostExitToken = StartIISExpress(uri, contentRoot); return(new DeploymentResult { ContentRoot = contentRoot, DeploymentParameters = DeploymentParameters, // Right now this works only for urls like http://localhost:5001/. Does not work for http://localhost:5001/subpath. ApplicationBaseUri = uri.ToString(), HostShutdownToken = hostExitToken }); }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("Deployment")) { 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; if (DeploymentParameters.ApplicationType == ApplicationType.Portable) { DefaultWebConfigActions.Add( WebConfigHelpers.AddOrModifyAspNetCoreSection( "processPath", 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); } var uri = TestUriHelper.BuildTestUri(ServerType.IIS, DeploymentParameters.ApplicationBaseUriHint); // To prevent modifying the IIS setup concurrently. await _application.StartIIS(uri, contentRoot); // Warm up time for IIS setup. Logger.LogInformation("Successfully finished IIS application directory setup."); return(new IISDeploymentResult( LoggerFactory, IISDeploymentParameters, applicationBaseUri: uri.ToString(), contentRoot: contentRoot, hostShutdownToken: _hostShutdownToken.Token, hostProcess: _application.HostProcess )); } }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("SelfHost.Deploy")) { // Start timer StartTimer(); if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.Clr && DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86) { // Publish is required to rebuild for the right bitness DeploymentParameters.PublishApplicationBeforeDeployment = true; } if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr && DeploymentParameters.ApplicationType == ApplicationType.Standalone) { // Publish is required to get the correct files in the output directory DeploymentParameters.PublishApplicationBeforeDeployment = true; } if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } // Launch the host process. for (var i = 0; i < RetryCount; i++) { var hintUrl = TestUriHelper.BuildTestUri( DeploymentParameters.ServerType, DeploymentParameters.Scheme, DeploymentParameters.ApplicationBaseUriHint, DeploymentParameters.StatusMessagesEnabled); var(actualUrl, hostExitToken) = await StartSelfHostAsync(hintUrl); if (DeploymentParameters.ServerType == ServerType.HttpSys && hostExitToken.IsCancellationRequested) { // Retry HttpSys deployments due to port conflicts. continue; } Logger.LogInformation("Application ready at URL: {appUrl}", actualUrl); return(new DeploymentResult( LoggerFactory, DeploymentParameters, applicationBaseUri: actualUrl.ToString(), contentRoot: DeploymentParameters.PublishApplicationBeforeDeployment ? DeploymentParameters.PublishedApplicationRootPath : DeploymentParameters.ApplicationPath, hostShutdownToken: hostExitToken)); } throw new Exception($"Failed to start Self hosted application after {RetryCount} retries."); } }
public override Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("Deployment")) { StartTimer(); Logger.LogInformation(Environment.OSVersion.ToString()); if (string.IsNullOrEmpty(DeploymentParameters.ServerConfigTemplateContent)) { DeploymentParameters.ServerConfigTemplateContent = File.ReadAllText("IIS.config"); } // For now, only support using published output DeploymentParameters.PublishApplicationBeforeDeployment = true; // Move ASPNETCORE_DETAILEDERRORS to web config env variables if (IISDeploymentParameters.EnvironmentVariables.ContainsKey(DetailedErrorsEnvironmentVariable)) { IISDeploymentParameters.WebConfigBasedEnvironmentVariables[DetailedErrorsEnvironmentVariable] = IISDeploymentParameters.EnvironmentVariables[DetailedErrorsEnvironmentVariable]; IISDeploymentParameters.EnvironmentVariables.Remove(DetailedErrorsEnvironmentVariable); } // Do not override settings set on parameters if (!IISDeploymentParameters.HandlerSettings.ContainsKey("debugLevel") && !IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile")) { _debugLogFile = Path.GetTempFileName(); IISDeploymentParameters.HandlerSettings["debugLevel"] = "file"; IISDeploymentParameters.HandlerSettings["debugFile"] = _debugLogFile; } DotnetPublish(); var contentRoot = DeploymentParameters.PublishedApplicationRootPath; RunWebConfigActions(contentRoot); var uri = TestUriHelper.BuildTestUri(ServerType.IIS, DeploymentParameters.ApplicationBaseUriHint); StartIIS(uri, contentRoot); // Warm up time for IIS setup. Logger.LogInformation("Successfully finished IIS application directory setup."); return(Task.FromResult <DeploymentResult>(new IISDeploymentResult( LoggerFactory, IISDeploymentParameters, applicationBaseUri: uri.ToString(), contentRoot: contentRoot, hostShutdownToken: _hostShutdownToken.Token, hostProcess: HostProcess ))); } }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("Deploy")) { _configFile = Path.GetTempFileName(); var uri = string.IsNullOrEmpty(DeploymentParameters.ApplicationBaseUriHint) ? TestUriHelper.BuildTestUri(ServerType.Nginx) : new Uri(DeploymentParameters.ApplicationBaseUriHint); var redirectUri = TestUriHelper.BuildTestUri(ServerType.Nginx); if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr && DeploymentParameters.ApplicationType == ApplicationType.Standalone) { // Publish is required to get the correct files in the output directory DeploymentParameters.PublishApplicationBeforeDeployment = true; } if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } var(appUri, exitToken) = await StartSelfHostAsync(redirectUri); SetupNginx(appUri.ToString(), uri); Logger.LogInformation("Application ready at URL: {appUrl}", 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 = await RetryHelper.RetryRequest(() => { return(httpClient.GetAsync(redirectUri)); }, Logger, exitToken); if (!response.IsSuccessStatusCode) { throw new InvalidOperationException("Deploy failed"); } } return(new DeploymentResult( LoggerFactory, DeploymentParameters, applicationBaseUri: uri.ToString(), contentRoot: DeploymentParameters.ApplicationPath, hostShutdownToken: exitToken)); } }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("SelfHost.Deploy")) { // Start timer StartTimer(); if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.Clr && DeploymentParameters.RuntimeArchitecture == RuntimeArchitecture.x86) { // Publish is required to rebuild for the right bitness DeploymentParameters.PublishApplicationBeforeDeployment = true; } if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr && DeploymentParameters.ApplicationType == ApplicationType.Standalone) { // Publish is required to get the correct files in the output directory DeploymentParameters.PublishApplicationBeforeDeployment = true; } if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } var hintUrl = TestUriHelper.BuildTestUri( DeploymentParameters.ServerType, DeploymentParameters.Scheme, DeploymentParameters.ApplicationBaseUriHint, DeploymentParameters.StatusMessagesEnabled); // Launch the host process. var(actualUrl, hostExitToken) = await StartSelfHostAsync(hintUrl); Logger.LogInformation("Application ready at URL: {appUrl}", actualUrl); return(new DeploymentResult( LoggerFactory, DeploymentParameters, applicationBaseUri: actualUrl.ToString(), contentRoot: DeploymentParameters.PublishApplicationBeforeDeployment ? DeploymentParameters.PublishedApplicationRootPath : DeploymentParameters.ApplicationPath, hostShutdownToken: hostExitToken)); } }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("Deployment")) { StartTimer(); var contentRoot = string.Empty; if (string.IsNullOrEmpty(DeploymentParameters.ServerConfigTemplateContent)) { DeploymentParameters.ServerConfigTemplateContent = File.ReadAllText("IIS.config"); } _application = new IISApplication(DeploymentParameters, Logger); // For now, only support using published output DeploymentParameters.PublishApplicationBeforeDeployment = true; if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); contentRoot = DeploymentParameters.PublishedApplicationRootPath; } WebConfigHelpers.AddDebugLogToWebConfig(contentRoot, Path.Combine(contentRoot, $"{_application.WebSiteName}.txt")); var uri = TestUriHelper.BuildTestUri(ServerType.IIS, DeploymentParameters.ApplicationBaseUriHint); // To prevent modifying the IIS setup concurrently. await _application.StartIIS(uri, contentRoot); // Warm up time for IIS setup. Logger.LogInformation("Successfully finished IIS application directory setup."); return(new DeploymentResult( LoggerFactory, DeploymentParameters, applicationBaseUri: uri.ToString(), contentRoot: contentRoot, hostShutdownToken: _hostShutdownToken.Token )); } }
public override DeploymentResult Deploy() { // Start timer StartTimer(); if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } var uri = TestUriHelper.BuildTestUri(DeploymentParameters.ApplicationBaseUriHint); // Launch the host process. var hostExitToken = StartSelfHost(uri); return(new DeploymentResult { WebRootLocation = DeploymentParameters.ApplicationPath, DeploymentParameters = DeploymentParameters, ApplicationBaseUri = uri.ToString(), HostShutdownToken = hostExitToken }); }
public override DeploymentResult Deploy() { // Start timer StartTimer(); // Only supports publish and run on IIS. DeploymentParameters.PublishApplicationBeforeDeployment = true; _application = new IISApplication(DeploymentParameters, Logger); // Publish to IIS root\application folder. DotnetPublish(publishRoot: _application.WebSiteRootFolder); // Drop a json file instead of setting environment variable. SetAspEnvironmentWithJson(); var uri = TestUriHelper.BuildTestUri(); lock (_syncObject) { // To prevent modifying the IIS setup concurrently. _application.Deploy(uri); } // Warm up time for IIS setup. Thread.Sleep(1 * 1000); Logger.LogInformation("Successfully finished IIS application directory setup."); return(new DeploymentResult { WebRootLocation = DeploymentParameters.ApplicationPath, DeploymentParameters = DeploymentParameters, // Accomodate the vdir name. ApplicationBaseUri = uri.ToString(), HostShutdownToken = _hostShutdownToken.Token }); }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("Deployment")) { // Start timer StartTimer(); // For an unpublished application the dllroot points pre-built dlls like projectdir/bin/debug/netcoreapp3.0/ // and contentRoot points to the project directory so you get things like static assets. // For a published app both point to the publish directory. var dllRoot = CheckIfPublishIsRequired(); var contentRoot = string.Empty; if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); contentRoot = DeploymentParameters.PublishedApplicationRootPath; } else { // Core+Standalone always publishes. This must be Clr+Standalone or Core+Portable. // Update processPath and arguments for our current scenario contentRoot = DeploymentParameters.ApplicationPath; var executableExtension = DeploymentParameters.ApplicationType == ApplicationType.Portable ? ".dll" : ".exe"; var entryPoint = Path.Combine(dllRoot, DeploymentParameters.ApplicationName + executableExtension); var executableName = string.Empty; var executableArgs = string.Empty; if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr && DeploymentParameters.ApplicationType == ApplicationType.Portable) { executableName = GetDotNetExeForArchitecture(); executableArgs = entryPoint; } else { executableName = entryPoint; } Logger.LogInformation("Executing: {exe} {args}", executableName, executableArgs); DeploymentParameters.EnvironmentVariables["LAUNCHER_PATH"] = executableName; DeploymentParameters.EnvironmentVariables["LAUNCHER_ARGS"] = executableArgs; // CurrentDirectory will point to bin/{config}/{tfm}, but the config and static files aren't copied, point to the app base instead. Logger.LogInformation("ContentRoot: {path}", DeploymentParameters.ApplicationPath); DeploymentParameters.EnvironmentVariables["ASPNETCORE_CONTENTROOT"] = DeploymentParameters.ApplicationPath; } RunWebConfigActions(contentRoot); var testUri = TestUriHelper.BuildTestUri(ServerType.IISExpress, DeploymentParameters.ApplicationBaseUriHint); // Launch the host process. var(actualUri, hostExitToken) = await StartIISExpressAsync(testUri, contentRoot); Logger.LogInformation("Application ready at URL: {appUrl}", actualUri); // Right now this works only for urls like http://localhost:5001/. Does not work for http://localhost:5001/subpath. return(new IISDeploymentResult( LoggerFactory, IISDeploymentParameters, applicationBaseUri: actualUri.ToString(), contentRoot: contentRoot, hostShutdownToken: hostExitToken, hostProcess: _hostProcess)); } }
private async Task <(Uri url, CancellationToken hostExitToken)> StartIISExpressAsync(string contentRoot) { using (Logger.BeginScope("StartIISExpress")) { var iisExpressPath = GetIISExpressPath(); for (var attempt = 0; attempt < MaximumAttempts; attempt++) { var uri = TestUriHelper.BuildTestUri(ServerType.IISExpress, DeploymentParameters.ApplicationBaseUriHint); var port = uri.Port; if (port == 0) { port = (uri.Scheme == "https") ? TestPortHelper.GetNextSSLPort() : TestPortHelper.GetNextPort(); } Logger.LogInformation("Attempting to start IIS Express on port: {port}", port); PrepareConfig(contentRoot, port); var parameters = string.IsNullOrEmpty(DeploymentParameters.ServerConfigLocation) ? string.Format(CultureInfo.InvariantCulture, "/port:{0} /path:\"{1}\" /trace:error /systray:false", uri.Port, contentRoot) : string.Format(CultureInfo.InvariantCulture, "/site:{0} /config:{1} /trace:error /systray:false", DeploymentParameters.SiteName, DeploymentParameters.ServerConfigLocation); Logger.LogInformation("Executing command : {iisExpress} {parameters}", iisExpressPath, parameters); var startInfo = new ProcessStartInfo { FileName = iisExpressPath, Arguments = parameters, UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, // VS sets current directory to C:\Program Files\IIS Express WorkingDirectory = Path.GetDirectoryName(iisExpressPath) }; AddEnvironmentVariablesToProcess(startInfo, DeploymentParameters.EnvironmentVariables); Uri url = null; var started = new TaskCompletionSource <bool>(); var process = new Process() { StartInfo = startInfo }; process.OutputDataReceived += (sender, dataArgs) => { if (string.Equals(dataArgs.Data, UnableToStartIISExpressMessage, StringComparison.Ordinal)) { // We completely failed to start and we don't really know why started.TrySetException(new InvalidOperationException("Failed to start IIS Express")); } else if (string.Equals(dataArgs.Data, FailedToInitializeBindingsMessage, StringComparison.Ordinal)) { started.TrySetResult(false); } else if (string.Equals(dataArgs.Data, IISExpressRunningMessage, StringComparison.Ordinal)) { started.TrySetResult(true); } else if (!string.IsNullOrEmpty(dataArgs.Data)) { var m = UrlDetectorRegex.Match(dataArgs.Data); if (m.Success) { url = new Uri(m.Groups["url"].Value); } } }; process.EnableRaisingEvents = true; var hostExitTokenSource = new CancellationTokenSource(); process.Exited += (sender, e) => { Logger.LogInformation("iisexpress Process {pid} shut down", process.Id); // If TrySetResult was called above, this will just silently fail to set the new state, which is what we want started.TrySetException(new Exception($"Command exited unexpectedly with exit code: {process.ExitCode}")); TriggerHostShutdown(hostExitTokenSource); }; process.StartAndCaptureOutAndErrToLogger("iisexpress", Logger); Logger.LogInformation("iisexpress Process {pid} started", process.Id); if (process.HasExited) { Logger.LogError("Host process {processName} {pid} exited with code {exitCode} or failed to start.", startInfo.FileName, process.Id, process.ExitCode); throw new Exception("Failed to start host"); } // Wait for the app to start // The timeout here is large, because we don't know how long the test could need // We cover a lot of error cases above, but I want to make sure we eventually give up and don't hang the build // just in case we missed one -anurse if (!await started.Task.TimeoutAfter(TimeSpan.FromMinutes(10))) { Logger.LogInformation("iisexpress Process {pid} failed to bind to port {port}, trying again", process.Id, port); // Wait for the process to exit and try again process.WaitForExit(30 * 1000); await Task.Delay(1000); // Wait a second to make sure the socket is completely cleaned up } else { _hostProcess = process; // Ensure iisexpress.exe is killed if test process termination is non-graceful. // Prevents locked files when stop debugging unit test. ProcessTracker.Add(_hostProcess); // cache the process start time for verifying log file name. var _ = _hostProcess.StartTime; Logger.LogInformation("Started iisexpress successfully. Process Id : {processId}, Port: {port}", _hostProcess.Id, port); return(url : url, hostExitToken : hostExitTokenSource.Token); } } var message = $"Failed to initialize IIS Express after {MaximumAttempts} attempts to select a port"; Logger.LogError(message); throw new TimeoutException(message); } }
public override async Task <DeploymentResult> DeployAsync() { using (Logger.BeginScope("Deploy")) { _configFile = Path.GetTempFileName(); var uri = string.IsNullOrEmpty(DeploymentParameters.ApplicationBaseUriHint) ? new Uri("http://localhost:0") : new Uri(DeploymentParameters.ApplicationBaseUriHint); if (uri.Port == 0) { var builder = new UriBuilder(uri); if (OperatingSystem.IsLinux()) { // This works with nginx 1.9.1 and later using the reuseport flag, available on Ubuntu 16.04. // Keep it open so nobody else claims the port _portSelector = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _portSelector.Bind(new IPEndPoint(IPAddress.Loopback, 0)); builder.Port = ((IPEndPoint)_portSelector.LocalEndPoint).Port; } else { builder.Port = TestPortHelper.GetNextPort(); } uri = builder.Uri; } var redirectUri = TestUriHelper.BuildTestUri(ServerType.Nginx); if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr && DeploymentParameters.ApplicationType == ApplicationType.Standalone) { // Publish is required to get the correct files in the output directory DeploymentParameters.PublishApplicationBeforeDeployment = true; } if (DeploymentParameters.PublishApplicationBeforeDeployment) { DotnetPublish(); } var(appUri, exitToken) = await StartSelfHostAsync(redirectUri); SetupNginx(appUri.ToString(), uri); Logger.LogInformation("Application ready at URL: {appUrl}", 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 = await RetryHelper.RetryRequest(() => { return(httpClient.GetAsync(redirectUri)); }, Logger, exitToken); if (!response.IsSuccessStatusCode) { throw new InvalidOperationException("Deploy failed"); } } return(new DeploymentResult( LoggerFactory, DeploymentParameters, applicationBaseUri: uri.ToString(), contentRoot: DeploymentParameters.ApplicationPath, hostShutdownToken: exitToken)); } }
public static string GetTestUrl(ServerType serverType) { return(TestUriHelper.BuildTestUri(serverType).ToString()); }