Start adding configuration reload tests (#1212)
This commit is contained in:
parent
f1c1f82e8b
commit
d725972355
|
|
@ -47,8 +47,9 @@ PrintDebugHeader()
|
||||||
RETURN_LAST_ERROR_IF(!VerQueryValue(verData.data(), _T("\\StringFileInfo\\040904b0\\FileDescription"), &pvProductName, &iProductNameLen));
|
RETURN_LAST_ERROR_IF(!VerQueryValue(verData.data(), _T("\\StringFileInfo\\040904b0\\FileDescription"), &pvProductName, &iProductNameLen));
|
||||||
|
|
||||||
// Major, minor are stored in dwFileVersionMS field and patch, build in dwFileVersionLS field as pair of 32 bit numbers
|
// Major, minor are stored in dwFileVersionMS field and patch, build in dwFileVersionLS field as pair of 32 bit numbers
|
||||||
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "Initializing logs for %S. File Version: %d.%d.%d.%d. Description: %S",
|
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "Initializing logs for %S. ProcessId: %d. File Version: %d.%d.%d.%d. Description: %S",
|
||||||
path,
|
path,
|
||||||
|
GetCurrentProcessId(),
|
||||||
( verInfo->dwFileVersionMS >> 16 ) & 0xffff,
|
( verInfo->dwFileVersionMS >> 16 ) & 0xffff,
|
||||||
( verInfo->dwFileVersionMS >> 0 ) & 0xffff,
|
( verInfo->dwFileVersionMS >> 0 ) & 0xffff,
|
||||||
( verInfo->dwFileVersionLS >> 16 ) & 0xffff,
|
( verInfo->dwFileVersionLS >> 16 ) & 0xffff,
|
||||||
|
|
|
||||||
|
|
@ -123,47 +123,61 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||||
|
|
||||||
private void GetLogsFromFile()
|
private void GetLogsFromFile()
|
||||||
{
|
{
|
||||||
// Handle cases where debug file is redirected by test
|
try
|
||||||
var debugLogLocations = new List<string>();
|
|
||||||
if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
|
|
||||||
{
|
{
|
||||||
debugLogLocations.Add(IISDeploymentParameters.HandlerSettings["debugFile"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DeploymentParameters.EnvironmentVariables.ContainsKey("ASPNETCORE_MODULE_DEBUG_FILE"))
|
// Handle cases where debug file is redirected by test
|
||||||
{
|
var debugLogLocations = new List<string>();
|
||||||
debugLogLocations.Add(DeploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"]);
|
if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
|
||||||
}
|
|
||||||
|
|
||||||
// default debug file name
|
|
||||||
debugLogLocations.Add("aspnetcore-debug.log");
|
|
||||||
|
|
||||||
foreach (var debugLogLocation in debugLogLocations)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(debugLogLocation))
|
|
||||||
{
|
{
|
||||||
continue;
|
debugLogLocations.Add(IISDeploymentParameters.HandlerSettings["debugFile"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var file = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, debugLogLocation);
|
if (DeploymentParameters.EnvironmentVariables.ContainsKey("ASPNETCORE_MODULE_DEBUG_FILE"))
|
||||||
if (File.Exists(file))
|
|
||||||
{
|
{
|
||||||
var lines = File.ReadAllLines(file);
|
debugLogLocations.Add(DeploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"]);
|
||||||
if (!lines.Any())
|
}
|
||||||
|
|
||||||
|
// default debug file name
|
||||||
|
debugLogLocations.Add("aspnetcore-debug.log");
|
||||||
|
|
||||||
|
foreach (var debugLogLocation in debugLogLocations)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(debugLogLocation))
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Debug log file found but was empty");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var line in lines)
|
var file = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, debugLogLocation);
|
||||||
|
if (File.Exists(file))
|
||||||
{
|
{
|
||||||
Logger.LogInformation(line);
|
var lines = File.ReadAllLines(file);
|
||||||
|
if (!lines.Any())
|
||||||
|
{
|
||||||
|
Logger.LogInformation($"Debug log file {file} found but was empty");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
Logger.LogInformation(line);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
}
|
||||||
|
// ANCM V1 does not support logs
|
||||||
|
if (DeploymentParameters.AncmVersion == AncmVersion.AspNetCoreModuleV2)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Unable to find non-empty debug log files. Tried: {string.Join(", ", debugLogLocations)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(_debugLogFile))
|
||||||
|
{
|
||||||
|
File.Delete(_debugLogFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException($"Not able to find non-empty debug log files. Tried: {string.Join(", ", debugLogLocations)}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartIIS(Uri uri, string contentRoot)
|
public void StartIIS(Uri uri, string contentRoot)
|
||||||
|
|
@ -339,6 +353,12 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||||
throw new InvalidOperationException("Site not stopped yet");
|
throw new InvalidOperationException("Site not stopped yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (appPool.WorkerProcesses.Any(wp => wp.State == WorkerProcessState.Running ||
|
||||||
|
wp.State == WorkerProcessState.Stopping))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("WorkerProcess not stopped yet");
|
||||||
|
}
|
||||||
|
|
||||||
if (!HostProcess.HasExited)
|
if (!HostProcess.HasExited)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Site is stopped but host process is not");
|
throw new InvalidOperationException("Site is stopped but host process is not");
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,6 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[RequiresIIS(IISCapability.ShutdownToken)]
|
|
||||||
public async Task AppOfflineDroppedWhileSiteFailedToStartInRequestHandler_SiteStops_InProcess()
|
public async Task AppOfflineDroppedWhileSiteFailedToStartInRequestHandler_SiteStops_InProcess()
|
||||||
{
|
{
|
||||||
var deploymentResult = await DeployApp(HostingModel.InProcess);
|
var deploymentResult = await DeployApp(HostingModel.InProcess);
|
||||||
|
|
@ -95,7 +94,8 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
Assert.Contains("500.30", await result.Content.ReadAsStringAsync());
|
Assert.Contains("500.30", await result.Content.ReadAsStringAsync());
|
||||||
|
|
||||||
AddAppOffline(deploymentResult.ContentRoot);
|
AddAppOffline(deploymentResult.ContentRoot);
|
||||||
AssertStopsProcess(deploymentResult);
|
|
||||||
|
await deploymentResult.AssertRecycledAsync(() => AssertAppOffline(deploymentResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
|
|
@ -130,7 +130,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
AssertStopsProcess(deploymentResult);
|
deploymentResult.AssertWorkerProcessStop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,18 +139,16 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[RequiresIIS(IISCapability.ShutdownToken)]
|
|
||||||
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_InProcess()
|
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_InProcess()
|
||||||
{
|
{
|
||||||
var deploymentResult = await AssertStarts(HostingModel.InProcess);
|
var deploymentResult = await AssertStarts(HostingModel.InProcess);
|
||||||
|
|
||||||
AddAppOffline(deploymentResult.ContentRoot);
|
AddAppOffline(deploymentResult.ContentRoot);
|
||||||
|
|
||||||
AssertStopsProcess(deploymentResult);
|
await deploymentResult.AssertRecycledAsync(() => AssertAppOffline(deploymentResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[RequiresIIS(IISCapability.ShutdownToken)]
|
|
||||||
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_OutOfProcess()
|
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_OutOfProcess()
|
||||||
{
|
{
|
||||||
var deploymentResult = await AssertStarts(HostingModel.OutOfProcess);
|
var deploymentResult = await AssertStarts(HostingModel.OutOfProcess);
|
||||||
|
|
@ -245,28 +243,10 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
|
|
||||||
private async Task AssertAppOffline(IISDeploymentResult deploymentResult, string expectedResponse = "The app is offline.")
|
private async Task AssertAppOffline(IISDeploymentResult deploymentResult, string expectedResponse = "The app is offline.")
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = null;
|
var response = await deploymentResult.HttpClient.RetryRequestAsync("HelloWorld", r => r.StatusCode == HttpStatusCode.ServiceUnavailable);
|
||||||
|
|
||||||
for (var i = 0; i < 5 && response?.StatusCode != HttpStatusCode.ServiceUnavailable; i++)
|
|
||||||
{
|
|
||||||
// Keep retrying until app_offline is present.
|
|
||||||
response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
|
||||||
await Task.Delay(RetryDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
|
|
||||||
|
|
||||||
Assert.Equal(expectedResponse, await response.Content.ReadAsStringAsync());
|
Assert.Equal(expectedResponse, await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AssertStopsProcess(IISDeploymentResult deploymentResult)
|
|
||||||
{
|
|
||||||
var hostShutdownToken = deploymentResult.HostShutdownToken;
|
|
||||||
|
|
||||||
Assert.True(hostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
|
|
||||||
Assert.True(hostShutdownToken.IsCancellationRequested);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<IISDeploymentResult> AssertStarts(HostingModel hostingModel)
|
private async Task<IISDeploymentResult> AssertStarts(HostingModel hostingModel)
|
||||||
{
|
{
|
||||||
var deploymentResult = await DeployApp(hostingModel);
|
var deploymentResult = await DeployApp(hostingModel);
|
||||||
|
|
@ -278,14 +258,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
||||||
|
|
||||||
private static async Task AssertRunning(IISDeploymentResult deploymentResult)
|
private static async Task AssertRunning(IISDeploymentResult deploymentResult)
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = null;
|
var response = await deploymentResult.HttpClient.RetryRequestAsync("HelloWorld", r => r.IsSuccessStatusCode);
|
||||||
|
|
||||||
for (var i = 0; i < 5 && response?.IsSuccessStatusCode != true; i++)
|
|
||||||
{
|
|
||||||
response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
|
||||||
await Task.Delay(RetryDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal("Hello World", responseText);
|
Assert.Equal("Hello World", responseText);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
// 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.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
{
|
||||||
|
[Collection(PublishedSitesCollection.Name)]
|
||||||
|
public class ConfigurationChangeTests : IISFunctionalTestBase
|
||||||
|
{
|
||||||
|
private readonly PublishedSitesFixture _fixture;
|
||||||
|
|
||||||
|
public ConfigurationChangeTests(PublishedSitesFixture fixture)
|
||||||
|
{
|
||||||
|
_fixture = fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task ConfigurationChangeStopsInProcess()
|
||||||
|
{
|
||||||
|
var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.InProcess, publish: true);
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
|
await deploymentResult.AssertStarts();
|
||||||
|
|
||||||
|
// Just "touching" web.config should be enough
|
||||||
|
deploymentResult.ModifyWebConfig(element => {});
|
||||||
|
|
||||||
|
await deploymentResult.AssertRecycledAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalTheory]
|
||||||
|
[InlineData(AncmVersion.AspNetCoreModule)]
|
||||||
|
[InlineData(AncmVersion.AspNetCoreModuleV2)]
|
||||||
|
public async Task ConfigurationChangeForcesChildProcessRestart(AncmVersion version)
|
||||||
|
{
|
||||||
|
var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess, publish: true);
|
||||||
|
deploymentParameters.AncmVersion = version;
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
|
var processBefore = await deploymentResult.HttpClient.GetStringAsync("/ProcessId");
|
||||||
|
|
||||||
|
// Just "touching" web.config should be enough
|
||||||
|
deploymentResult.ModifyWebConfig(element => {});
|
||||||
|
|
||||||
|
// Have to retry here to allow ANCM to receive notification and react to it
|
||||||
|
// Verify that worker process gets restarted with new process id
|
||||||
|
await deploymentResult.HttpClient.RetryRequestAsync("/ProcessId", async r => await r.Content.ReadAsStringAsync() != processBefore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
@ -18,6 +17,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
{
|
{
|
||||||
public static class Helpers
|
public static class Helpers
|
||||||
{
|
{
|
||||||
|
private static readonly TimeSpan RetryRequestDelay = TimeSpan.FromMilliseconds(100);
|
||||||
|
private static readonly int RetryRequestCount = 5;
|
||||||
|
|
||||||
public static string GetTestWebSitePath(string name)
|
public static string GetTestWebSitePath(string name)
|
||||||
{
|
{
|
||||||
return Path.Combine(TestPathUtilities.GetSolutionRootDirectory("IISIntegration"),"test", "WebSites", name);
|
return Path.Combine(TestPathUtilities.GetSolutionRootDirectory("IISIntegration"),"test", "WebSites", name);
|
||||||
|
|
@ -26,9 +28,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
public static string GetInProcessTestSitesPath() => GetTestWebSitePath("InProcessWebSite");
|
public static string GetInProcessTestSitesPath() => GetTestWebSitePath("InProcessWebSite");
|
||||||
|
|
||||||
public static string GetOutOfProcessTestSitesPath() => GetTestWebSitePath("OutOfProcessWebSite");
|
public static string GetOutOfProcessTestSitesPath() => GetTestWebSitePath("OutOfProcessWebSite");
|
||||||
|
|
||||||
|
|
||||||
public static async Task AssertStarts(IISDeploymentResult deploymentResult, string path = "/HelloWorld")
|
public static async Task AssertStarts(this IISDeploymentResult deploymentResult, string path = "/HelloWorld")
|
||||||
{
|
{
|
||||||
var response = await deploymentResult.HttpClient.GetAsync(path);
|
var response = await deploymentResult.HttpClient.GetAsync(path);
|
||||||
|
|
||||||
|
|
@ -73,5 +74,64 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
fileInfo.CopyTo(destFileName);
|
fileInfo.CopyTo(destFileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ModifyWebConfig(this DeploymentResult deploymentResult, Action<XElement> action)
|
||||||
|
{
|
||||||
|
var webConfigPath = Path.Combine(deploymentResult.ContentRoot, "web.config");
|
||||||
|
var document = XDocument.Load(webConfigPath);
|
||||||
|
|
||||||
|
document.Save(webConfigPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<HttpResponseMessage> RetryRequestAsync(this HttpClient client, string uri, Func<HttpResponseMessage, bool> predicate)
|
||||||
|
{
|
||||||
|
return RetryRequestAsync(client, uri, message => Task.FromResult(predicate(message)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<HttpResponseMessage> RetryRequestAsync(this HttpClient client, string uri, Func<HttpResponseMessage, Task<bool>> predicate)
|
||||||
|
{
|
||||||
|
HttpResponseMessage response = await client.GetAsync(uri);
|
||||||
|
|
||||||
|
for (var i = 0; i < RetryRequestCount && !await predicate(response); i++)
|
||||||
|
{
|
||||||
|
// Keep retrying until app_offline is present.
|
||||||
|
response = await client.GetAsync(uri);
|
||||||
|
await Task.Delay(RetryRequestDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await predicate(response))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Didn't get response that satisfies predicate after {RetryRequestCount} retries");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AssertWorkerProcessStop(this IISDeploymentResult deploymentResult)
|
||||||
|
{
|
||||||
|
var hostProcess = deploymentResult.HostProcess;
|
||||||
|
Assert.True(hostProcess.WaitForExit((int)TimeoutExtensions.DefaultTimeout.TotalMilliseconds));
|
||||||
|
|
||||||
|
if (deploymentResult.DeploymentParameters.ServerType == ServerType.IISExpress)
|
||||||
|
{
|
||||||
|
Assert.Equal(0, hostProcess.ExitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task AssertRecycledAsync(this IISDeploymentResult deploymentResult, Func<Task> verificationAction = null)
|
||||||
|
{
|
||||||
|
if (deploymentResult.DeploymentParameters.HostingModel != HostingModel.InProcess)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
deploymentResult.AssertWorkerProcessStop();
|
||||||
|
if (deploymentResult.DeploymentParameters.ServerType == ServerType.IIS)
|
||||||
|
{
|
||||||
|
verificationAction = verificationAction ?? (() => deploymentResult.AssertStarts());
|
||||||
|
await verificationAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue