Start adding configuration reload tests (#1212)

This commit is contained in:
Pavel Krymets 2018-08-13 13:02:57 -07:00 committed by GitHub
parent f1c1f82e8b
commit d725972355
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 176 additions and 64 deletions

View File

@ -47,8 +47,9 @@ PrintDebugHeader()
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
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,
GetCurrentProcessId(),
( verInfo->dwFileVersionMS >> 16 ) & 0xffff,
( verInfo->dwFileVersionMS >> 0 ) & 0xffff,
( verInfo->dwFileVersionLS >> 16 ) & 0xffff,

View File

@ -123,47 +123,61 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
private void GetLogsFromFile()
{
// Handle cases where debug file is redirected by test
var debugLogLocations = new List<string>();
if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
try
{
debugLogLocations.Add(IISDeploymentParameters.HandlerSettings["debugFile"]);
}
if (DeploymentParameters.EnvironmentVariables.ContainsKey("ASPNETCORE_MODULE_DEBUG_FILE"))
{
debugLogLocations.Add(DeploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"]);
}
// default debug file name
debugLogLocations.Add("aspnetcore-debug.log");
foreach (var debugLogLocation in debugLogLocations)
{
if (string.IsNullOrEmpty(debugLogLocation))
// Handle cases where debug file is redirected by test
var debugLogLocations = new List<string>();
if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
{
continue;
debugLogLocations.Add(IISDeploymentParameters.HandlerSettings["debugFile"]);
}
var file = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, debugLogLocation);
if (File.Exists(file))
if (DeploymentParameters.EnvironmentVariables.ContainsKey("ASPNETCORE_MODULE_DEBUG_FILE"))
{
var lines = File.ReadAllLines(file);
if (!lines.Any())
debugLogLocations.Add(DeploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"]);
}
// 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;
}
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)
@ -339,6 +353,12 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
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)
{
throw new InvalidOperationException("Site is stopped but host process is not");

View File

@ -82,7 +82,6 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
}
[ConditionalFact]
[RequiresIIS(IISCapability.ShutdownToken)]
public async Task AppOfflineDroppedWhileSiteFailedToStartInRequestHandler_SiteStops_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());
AddAppOffline(deploymentResult.ContentRoot);
AssertStopsProcess(deploymentResult);
await deploymentResult.AssertRecycledAsync(() => AssertAppOffline(deploymentResult));
}
[ConditionalFact]
@ -130,7 +130,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
}
catch
{
AssertStopsProcess(deploymentResult);
deploymentResult.AssertWorkerProcessStop();
return;
}
}
@ -139,18 +139,16 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
}
[ConditionalFact]
[RequiresIIS(IISCapability.ShutdownToken)]
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_InProcess()
{
var deploymentResult = await AssertStarts(HostingModel.InProcess);
AddAppOffline(deploymentResult.ContentRoot);
AssertStopsProcess(deploymentResult);
await deploymentResult.AssertRecycledAsync(() => AssertAppOffline(deploymentResult));
}
[ConditionalFact]
[RequiresIIS(IISCapability.ShutdownToken)]
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_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.")
{
HttpResponseMessage response = null;
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);
var response = await deploymentResult.HttpClient.RetryRequestAsync("HelloWorld", r => r.StatusCode == HttpStatusCode.ServiceUnavailable);
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)
{
var deploymentResult = await DeployApp(hostingModel);
@ -278,14 +258,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
private static async Task AssertRunning(IISDeploymentResult deploymentResult)
{
HttpResponseMessage response = null;
for (var i = 0; i < 5 && response?.IsSuccessStatusCode != true; i++)
{
response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
await Task.Delay(RetryDelay);
}
var response = await deploymentResult.HttpClient.RetryRequestAsync("HelloWorld", r => r.IsSuccessStatusCode);
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
}

View File

@ -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);
}
}
}

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Linq;
@ -18,6 +17,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public static class Helpers
{
private static readonly TimeSpan RetryRequestDelay = TimeSpan.FromMilliseconds(100);
private static readonly int RetryRequestCount = 5;
public static string GetTestWebSitePath(string 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 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);
@ -73,5 +74,64 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
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();
}
}
}
}