Skip to content

Commit

Permalink
Fall back to relying on PATH when the dotnet executable is not found …
Browse files Browse the repository at this point in the history
…at the expected location (#5157)

Fall back to relying on PATH when the dotnet executable is not found at the expected location
  • Loading branch information
AnatoliB committed Oct 26, 2019
1 parent e96ca8d commit 3d13bf1
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void Configure(HttpWorkerOptions options)
{
throw new HostConfigurationException($"Missing WorkerDescription for HttpWorker");
}
httpWorkerDescription.ApplyDefaultsAndValidate(_scriptJobHostOptions.RootScriptPath);
httpWorkerDescription.ApplyDefaultsAndValidate(_scriptJobHostOptions.RootScriptPath, _logger);
options.Arguments = new WorkerProcessArguments()
{
ExecutablePath = options.Description.DefaultExecutablePath,
Expand Down
3 changes: 2 additions & 1 deletion src/WebJobs.Script/Workers/Http/HttpWorkerDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using Microsoft.Extensions.Logging;

namespace Microsoft.Azure.WebJobs.Script.Workers.Http
{
public class HttpWorkerDescription : WorkerDescription
{
public override void ApplyDefaultsAndValidate(string workerDirectory)
public override void ApplyDefaultsAndValidate(string workerDirectory, ILogger logger)
{
if (workerDirectory == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Collections.Generic;
using Microsoft.Extensions.Logging;

namespace Microsoft.Azure.WebJobs.Script.Workers
{
Expand All @@ -27,6 +28,6 @@ public abstract class WorkerDescription
/// </summary>
public List<string> Arguments { get; set; }

public abstract void ApplyDefaultsAndValidate(string workerDirectory);
public abstract void ApplyDefaultsAndValidate(string workerDirectory, ILogger logger);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ internal void AddProvider(string workerDir)
{
workerDescription.DefaultWorkerPath = GetHydratedWorkerPath(workerDescription);
}
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), _logger);
_workerDescripionDictionary[workerDescription.Language] = workerDescription;
}
catch (Exception ex)
Expand Down
27 changes: 23 additions & 4 deletions src/WebJobs.Script/Workers/Rpc/RpcWorkerDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc
Expand Down Expand Up @@ -65,7 +66,10 @@ public List<string> Extensions
}
}

public override void ApplyDefaultsAndValidate(string workerDirectory)
// Can be replaced for testing purposes
internal Func<string, bool> FileExists { private get; set; } = File.Exists;

public override void ApplyDefaultsAndValidate(string workerDirectory, ILogger logger)
{
if (workerDirectory == null)
{
Expand Down Expand Up @@ -94,7 +98,7 @@ public override void ApplyDefaultsAndValidate(string workerDirectory)
throw new FileNotFoundException($"Did not find {nameof(DefaultWorkerPath)} for language: {Language}");
}

ResolveDotNetDefaultExecutablePath();
ResolveDotNetDefaultExecutablePath(logger);
}

public void ValidateWorkerPath(string workerPath, OSPlatform os, Architecture architecture, string version)
Expand Down Expand Up @@ -139,14 +143,29 @@ private void ValidateRuntimeVersion(string version)
}
}

private void ResolveDotNetDefaultExecutablePath()
private void ResolveDotNetDefaultExecutablePath(ILogger logger)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
&& (DefaultExecutablePath.Equals(RpcWorkerConstants.DotNetExecutableName, StringComparison.OrdinalIgnoreCase)
|| DefaultExecutablePath.Equals(RpcWorkerConstants.DotNetExecutableNameWithExtension, StringComparison.OrdinalIgnoreCase)))
{
var programFilesFolder = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
DefaultExecutablePath = Path.Combine(programFilesFolder, RpcWorkerConstants.DotNetFolderName, DefaultExecutablePath);

var fullPath = Path.Combine(
programFilesFolder,
RpcWorkerConstants.DotNetFolderName,
RpcWorkerConstants.DotNetExecutableNameWithExtension);

if (FileExists(fullPath))
{
DefaultExecutablePath = fullPath;
}
else
{
logger.Log(
LogLevel.Warning,
$"File '{fullPath}' is not found, '{DefaultExecutablePath}' invocation will rely on the PATH environment variable.");
}
}
}
}
Expand Down
84 changes: 75 additions & 9 deletions test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,17 +244,21 @@ public void ReadWorkerProviderFromConfig_OverrideDefaultExePath()
[MemberData(nameof(InvalidWorkerDescriptions))]
public void InvalidWorkerDescription_Throws(WorkerDescription workerDescription)
{
Assert.Throws<ValidationException>(() => workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory()));
var testLogger = new TestLogger(testLanguage);

Assert.Throws<ValidationException>(() => workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger));
}

[Theory]
[MemberData(nameof(ValidWorkerDescriptions))]
public void ValidateWorkerDescription_Succeeds(WorkerDescription workerDescription)
{
var testLogger = new TestLogger(testLanguage);

try
{
RpcWorkerConfigTestUtilities.CreateTestWorkerFileInCurrentDir();
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
}
finally
{
Expand All @@ -267,26 +271,88 @@ public void ValidateWorkerDescription_Succeeds(WorkerDescription workerDescripti
[InlineData("DotNet")]
[InlineData("dotnet.exe")]
[InlineData("DOTNET.EXE")]
public void ValidateWorkerDescription_ResolvesDotNetDefaultWorkerExecutablePath(string defaultExecutablePath)
public void ValidateWorkerDescription_ResolvesDotNetDefaultWorkerExecutablePath_WhenExpectedFileExists(
string defaultExecutablePath)
{
var testLogger = new TestLogger(testLanguage);

var expectedExecutablePath =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", defaultExecutablePath)
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe")
: defaultExecutablePath;

var workerDescription = new RpcWorkerDescription { Language = testLanguage, Extensions = new List<string>(), DefaultExecutablePath = defaultExecutablePath };
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
var workerDescription = new RpcWorkerDescription
{
Language = testLanguage,
Extensions = new List<string>(),
DefaultExecutablePath = defaultExecutablePath,
FileExists = path =>
{
Assert.Equal(expectedExecutablePath, path);
return true;
}
};

workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
Assert.Equal(expectedExecutablePath, workerDescription.DefaultExecutablePath);
}

[Theory]
[InlineData("dotnet")]
[InlineData("DotNet")]
[InlineData("dotnet.exe")]
[InlineData("DOTNET.EXE")]
public void ValidateWorkerDescription_DoesNotModifyDefaultWorkerExecutablePathAndWarns_WhenExpectedFileDoesNotExist(
string defaultExecutablePath)
{
var testLogger = new TestLogger(testLanguage);

var expectedExecutablePath =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe")
: defaultExecutablePath;

var workerDescription = new RpcWorkerDescription
{
Language = testLanguage,
Extensions = new List<string>(),
DefaultExecutablePath = defaultExecutablePath,
FileExists = path =>
{
Assert.Equal(expectedExecutablePath, path);
return false;
}
};

workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
Assert.Equal(defaultExecutablePath, workerDescription.DefaultExecutablePath);
Assert.True(testLogger.GetLogMessages().Any(message => message.Level == LogLevel.Warning
&& message.FormattedMessage.Contains(defaultExecutablePath)
&& message.FormattedMessage.Contains(expectedExecutablePath)));
}

[Theory]
[InlineData(@"D:\CustomExecutableFolder\dotnet")]
[InlineData(@"/CustomExecutableFolder/dotnet")]
[InlineData("AnythingElse")]
public void ValidateWorkerDescription_DoesNotModifyDefaultWorkerExecutablePath_WhenDoesNotStrictlyMatchDotNet(string defaultExecutablePath)
public void ValidateWorkerDescription_DoesNotModifyDefaultWorkerExecutablePath_WhenDoesNotStrictlyMatchDotNet(
string defaultExecutablePath)
{
var workerDescription = new RpcWorkerDescription { Language = testLanguage, Extensions = new List<string>(), DefaultExecutablePath = defaultExecutablePath };
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
var testLogger = new TestLogger(testLanguage);

var workerDescription = new RpcWorkerDescription
{
Language = testLanguage,
Extensions = new List<string>(),
DefaultExecutablePath = defaultExecutablePath,
FileExists = path =>
{
Assert.True(false, "FileExists should not be called");
return false;
}
};

workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
Assert.Equal(defaultExecutablePath, workerDescription.DefaultExecutablePath);
}

Expand Down

0 comments on commit 3d13bf1

Please sign in to comment.