dotnet-installer/src/dotnet/BuildServer/BuildServerProvider.cs
Peter Huene b2b3947c68
Fix Razor server shutdown on Windows.
On Windows, the Razor server correctly creates the pid file with
`FileAccess.Write` and `FileOptions.DeleteOnClose`.  This requires a share mode
of `FileShare.Write | FileShare.Delete` to open.  However, the
`dotnet build-server shutdown` command was opening the file with
`FileShare.Read`.  As a result, an `IOException` was being thrown and was not
handled.

This change first opens the file with the appropriate share access and also
properly handles a failure to access or read the contents of the pid file.

Additionally, an integration test was added to test that Razor server shutdown
works as expected.

Fixes #9158.
2018-04-27 13:52:17 -07:00

107 lines
4.1 KiB
C#

// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.BuildServer
{
internal class BuildServerProvider : IBuildServerProvider
{
public const string PidFileDirectoryVariableName = "DOTNET_BUILD_PIDFILE_DIRECTORY";
private readonly IFileSystem _fileSystem;
private readonly IEnvironmentProvider _environmentProvider;
private readonly IReporter _reporter;
public BuildServerProvider(
IFileSystem fileSystem = null,
IEnvironmentProvider environmentProvider = null,
IReporter reporter = null)
{
_fileSystem = fileSystem ?? FileSystemWrapper.Default;
_environmentProvider = environmentProvider ?? new EnvironmentProvider();
_reporter = reporter ?? Reporter.Error;
}
public IEnumerable<IBuildServer> EnumerateBuildServers(ServerEnumerationFlags flags = ServerEnumerationFlags.All)
{
if ((flags & ServerEnumerationFlags.MSBuild) == ServerEnumerationFlags.MSBuild)
{
// Yield a single MSBuild server (handles server discovery itself)
// TODO: use pid file enumeration when supported by the server (https://github.com/dotnet/cli/issues/9113)
yield return new MSBuildServer();
}
if ((flags & ServerEnumerationFlags.VBCSCompiler) == ServerEnumerationFlags.VBCSCompiler)
{
// Yield a single VB/C# compiler (handles server discovery itself)
// TODO: use pid file enumeration when supported by the server (https://github.com/dotnet/cli/issues/9112)
yield return new VBCSCompilerServer();
}
// TODO: remove or amend this check when the following issues are resolved:
// https://github.com/dotnet/cli/issues/9112
// https://github.com/dotnet/cli/issues/9113
if ((flags & ServerEnumerationFlags.Razor) != ServerEnumerationFlags.Razor)
{
yield break;
}
var directory = GetPidFileDirectory();
if (!_fileSystem.Directory.Exists(directory.Value))
{
yield break;
}
foreach (var path in _fileSystem.Directory.EnumerateFiles(directory.Value, "*"))
{
if ((flags & ServerEnumerationFlags.Razor) == ServerEnumerationFlags.Razor &&
Path.GetFileName(path).StartsWith(RazorPidFile.FilePrefix))
{
var file = ReadRazorPidFile(new FilePath(path));
if (file != null)
{
yield return new RazorServer(file);
}
}
}
}
public DirectoryPath GetPidFileDirectory()
{
var directory = _environmentProvider.GetEnvironmentVariable(PidFileDirectoryVariableName);
if (!string.IsNullOrEmpty(directory))
{
return new DirectoryPath(directory);
}
return new DirectoryPath(
Path.Combine(
CliFolderPathCalculator.DotnetUserProfileFolderPath,
"pids",
"build"));
}
private RazorPidFile ReadRazorPidFile(FilePath path)
{
try
{
return RazorPidFile.Read(path, _fileSystem);
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
{
_reporter.WriteLine(
string.Format(
LocalizableStrings.FailedToReadPidFile,
path.Value,
ex.Message).Yellow());
return null;
}
}
}
}