Add module load location check (#94)

This commit is contained in:
Pavel Krymets 2017-08-28 10:56:50 -07:00 committed by GitHub
parent c9c21d2ce9
commit 5e3a7922fe
4 changed files with 177 additions and 13 deletions

View File

@ -9,12 +9,12 @@
<ItemGroup> <ItemGroup>
<None Include="Templates\*.*" CopyToOutputDirectory="PreserveNewest" /> <None Include="Templates\*.*" CopyToOutputDirectory="PreserveNewest" />
<ContentWithTargetPath Include="NuGet.config.template" CopyToOutputDirectory="PreserveNewest" TargetPath="NuGet.config"/> <ContentWithTargetPath Include="NuGet.config.template" CopyToOutputDirectory="PreserveNewest" TargetPath="NuGet.config" />
<ContentWithTargetPath Include="global.json.template" CopyToOutputDirectory="PreserveNewest" TargetPath="global.json"/> <ContentWithTargetPath Include="global.json.template" CopyToOutputDirectory="PreserveNewest" TargetPath="global.json" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(AspNetIntegrationTestingVersion)" /> <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(AspNetCoreVersion)" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(AspNetCoreVersion)" /> <PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(AspNetCoreVersion)" /> <PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(AspNetCoreVersion)" />

View File

@ -0,0 +1,40 @@
// 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.IO;
using System.Linq;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
public class PathUtilities
{
public static string[] GetStoreModules(string dotnetPath)
{
var dotnetHome = Path.GetDirectoryName(dotnetPath);
return new DirectoryInfo(Path.Combine(dotnetHome, "store", "x64", "netcoreapp2.0"))
.GetDirectories()
.Select(d => d.Name)
.ToArray();
}
public static string[] GetSharedRuntimeAssemblies(string dotnetPath)
{
var dotnetHome = Path.GetDirectoryName(dotnetPath);
return new DirectoryInfo(Path.Combine(dotnetHome, "shared", "Microsoft.NETCore.App"))
.GetDirectories()
.Single()
.GetFiles("*.dll")
.Select(f => f.Name)
.ToArray();
}
public static string GetBundledAspNetCoreVersion(string dotnetPath)
{
var dotnetHome = Path.GetDirectoryName(dotnetPath);
return new DirectoryInfo(Path.Combine(dotnetHome, "store", "x64", "netcoreapp2.0", "microsoft.aspnetcore"))
.GetDirectories()
.Single()
.Name;
}
}
}

View File

@ -7,8 +7,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
using Newtonsoft.Json;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
@ -17,6 +19,10 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
[Collection("Azure")] [Collection("Azure")]
public class TemplateFunctionalTests public class TemplateFunctionalTests
{ {
private const string RuntimeInformationMiddlewareType = "Microsoft.AspNetCore.AzureAppServices.FunctionalTests.RuntimeInformationMiddleware";
private const string RuntimeInformationMiddlewareFile = "Templates\\RuntimeInformationMiddleware.cs";
readonly AzureFixture _fixture; readonly AzureFixture _fixture;
private readonly ITestOutputHelper _outputHelper; private readonly ITestOutputHelper _outputHelper;
@ -43,6 +49,8 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
await dotnet.ExecuteAndAssertAsync("new " + template); await dotnet.ExecuteAndAssertAsync("new " + template);
InjectMiddlware(testDirectory, RuntimeInformationMiddlewareType, RuntimeInformationMiddlewareFile);
await site.BuildPublishProfileAsync(testDirectory.FullName); await site.BuildPublishProfileAsync(testDirectory.FullName);
await dotnet.ExecuteAndAssertAsync("publish /p:PublishProfile=Profile"); await dotnet.ExecuteAndAssertAsync("publish /p:PublishProfile=Profile");
@ -52,6 +60,12 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
var getResult = await httpClient.GetAsync("/"); var getResult = await httpClient.GetAsync("/");
getResult.EnsureSuccessStatusCode(); getResult.EnsureSuccessStatusCode();
Assert.Contains(expected, await getResult.Content.ReadAsStringAsync()); Assert.Contains(expected, await getResult.Content.ReadAsStringAsync());
getResult = await httpClient.GetAsync("/runtimeInfo");
getResult.EnsureSuccessStatusCode();
var runtimeInfo = JsonConvert.DeserializeObject<RuntimeInfo>(await getResult.Content.ReadAsStringAsync());
ValidateRuntimeInfo(runtimeInfo, dotnet.Command);
} }
} }
} }
@ -80,6 +94,8 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
await dotnet.ExecuteAndAssertAsync("new " + template); await dotnet.ExecuteAndAssertAsync("new " + template);
InjectMiddlware(testDirectory, RuntimeInformationMiddlewareType, RuntimeInformationMiddlewareFile);
FixAspNetCoreVersion(testDirectory, dotnet.Command); FixAspNetCoreVersion(testDirectory, dotnet.Command);
await dotnet.ExecuteAndAssertAsync("restore"); await dotnet.ExecuteAndAssertAsync("restore");
@ -93,23 +109,58 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
var getResult = await httpClient.GetAsync("/"); var getResult = await httpClient.GetAsync("/");
getResult.EnsureSuccessStatusCode(); getResult.EnsureSuccessStatusCode();
Assert.Contains(expected, await getResult.Content.ReadAsStringAsync()); Assert.Contains(expected, await getResult.Content.ReadAsStringAsync());
getResult = await httpClient.GetAsync("/runtimeInfo");
getResult.EnsureSuccessStatusCode();
var runtimeInfo = JsonConvert.DeserializeObject<RuntimeInfo>(await getResult.Content.ReadAsStringAsync());
ValidateRuntimeInfo(runtimeInfo, dotnet.Command);
} }
} }
} }
private void ValidateRuntimeInfo(RuntimeInfo runtimeInfo, string dotnetPath)
{
var storeModules = PathUtilities.GetStoreModules(dotnetPath);
var runtimeModules = PathUtilities.GetSharedRuntimeAssemblies(dotnetPath);
foreach (var runtimeInfoModule in runtimeInfo.Modules)
{
if (storeModules.Any(f => runtimeInfoModule.ModuleName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase)))
{
Assert.Contains("store\\x86\\netcoreapp2.0\\", runtimeInfoModule.FileName);
}
// Native modules would prefer to be loaded from windows folder, skip them
if (runtimeModules.Any(f => runtimeInfoModule.ModuleName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase)) &&
runtimeInfoModule.FileName.IndexOf("windows\\system32", StringComparison.InvariantCultureIgnoreCase) == -1)
{
Assert.Contains("shared\\Microsoft.NETCore.App\\", runtimeInfoModule.FileName);
}
}
}
private static void InjectMiddlware(DirectoryInfo projectRoot, string typeName, string fileName)
{
// Copy implementation file to project directory
var implementationFile = Path.Combine(Directory.GetCurrentDirectory(), fileName);
var destinationImplementationFile = Path.Combine(projectRoot.FullName, Path.GetFileName(fileName));
File.Copy(implementationFile, destinationImplementationFile, true);
// Register middleware in Startup.cs/Configure
var startupFile = Path.Combine(projectRoot.FullName, "Startup.cs");
var startupText = File.ReadAllText(startupFile);
startupText = Regex.Replace(startupText, "public void Configure\\([^{]+{", match => match.Value + $" app.UseMiddleware<{typeName}>();");
File.WriteAllText(startupFile, startupText);
}
private static void FixAspNetCoreVersion(DirectoryInfo testDirectory, string dotnetPath) private static void FixAspNetCoreVersion(DirectoryInfo testDirectory, string dotnetPath)
{ {
// TODO: Temporary workaround for broken templates in latest CLI // TODO: Temporary workaround for broken templates in latest CLI
// Detect what version of aspnet core was shipped with this CLI installation // Detect what version of aspnet core was shipped with this CLI installation
var aspnetCoreVersion = var aspnetCoreVersion = PathUtilities.GetBundledAspNetCoreVersion(dotnetPath);
new DirectoryInfo(
Path.Combine(
Path.GetDirectoryName(dotnetPath),
"store", "x64", "netcoreapp2.0", "microsoft.aspnetcore"))
.GetDirectories()
.Single()
.Name;
var csproj = testDirectory.GetFiles("*.csproj").Single().FullName; var csproj = testDirectory.GetFiles("*.csproj").Single().FullName;
var projectContents = XDocument.Load(csproj); var projectContents = XDocument.Load(csproj);
@ -149,14 +200,14 @@ namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
private TestCommand DotNet(TestLogger logger, DirectoryInfo workingDirectory, string sufix) private TestCommand DotNet(TestLogger logger, DirectoryInfo workingDirectory, string sufix)
{ {
return new TestCommand(GetDotnetPath(sufix)) return new TestCommand(GetDotNetPath(sufix))
{ {
Logger = logger, Logger = logger,
WorkingDirectory = workingDirectory.FullName WorkingDirectory = workingDirectory.FullName
}; };
} }
private static string GetDotnetPath(string sufix) private static string GetDotNetPath(string sufix)
{ {
var current = new DirectoryInfo(Directory.GetCurrentDirectory()); var current = new DirectoryInfo(Directory.GetCurrentDirectory());
while (current != null) while (current != null)

View File

@ -0,0 +1,73 @@
// 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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
public class ModuleInfo
{
public string FileName { get; set; }
public string ModuleName { get; set; }
public string Version { get; set; }
public string InformationalVersion { get; set; }
}
public class RuntimeInfo
{
public IDictionary Environment { get; set; }
public IList<ModuleInfo> Modules { get; set; }
}
class RuntimeInformationMiddleware
{
private readonly RequestDelegate _next;
public RuntimeInformationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path == "/runtimeInfo")
{
await context.Response.WriteAsync(
JsonConvert.SerializeObject(
new RuntimeInfo
{
Environment = Environment.GetEnvironmentVariables(),
Modules = Process.GetCurrentProcess().Modules.OfType<ProcessModule>().Select(m =>
{
Assembly assembly = null;
try
{
assembly = Assembly.Load(Path.GetFileNameWithoutExtension(m.ModuleName));
}
catch { }
return new ModuleInfo
{
FileName = m.FileName,
ModuleName = m.ModuleName,
Version = assembly?.GetName().Version.ToString(),
InformationalVersion = assembly?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
};
}).ToList()
}));
return;
}
// Call the next delegate/middleware in the pipeline
await _next(context);
}
}
}