Skip to content

Commit 3d13bf1

Browse files
authored
Fall back to relying on PATH when the dotnet executable is not found at the expected location (#5157)
Fall back to relying on PATH when the dotnet executable is not found at the expected location
1 parent e96ca8d commit 3d13bf1

File tree

6 files changed

+104
-17
lines changed

6 files changed

+104
-17
lines changed

src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptionsSetup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public void Configure(HttpWorkerOptions options)
3636
{
3737
throw new HostConfigurationException($"Missing WorkerDescription for HttpWorker");
3838
}
39-
httpWorkerDescription.ApplyDefaultsAndValidate(_scriptJobHostOptions.RootScriptPath);
39+
httpWorkerDescription.ApplyDefaultsAndValidate(_scriptJobHostOptions.RootScriptPath, _logger);
4040
options.Arguments = new WorkerProcessArguments()
4141
{
4242
ExecutablePath = options.Description.DefaultExecutablePath,

src/WebJobs.Script/Workers/Http/HttpWorkerDescription.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
using System.Collections.Generic;
66
using System.ComponentModel.DataAnnotations;
77
using System.IO;
8+
using Microsoft.Extensions.Logging;
89

910
namespace Microsoft.Azure.WebJobs.Script.Workers.Http
1011
{
1112
public class HttpWorkerDescription : WorkerDescription
1213
{
13-
public override void ApplyDefaultsAndValidate(string workerDirectory)
14+
public override void ApplyDefaultsAndValidate(string workerDirectory, ILogger logger)
1415
{
1516
if (workerDirectory == null)
1617
{

src/WebJobs.Script/Workers/ProcessManagement/WorkerDescription.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System.Collections.Generic;
5+
using Microsoft.Extensions.Logging;
56

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

30-
public abstract void ApplyDefaultsAndValidate(string workerDirectory);
31+
public abstract void ApplyDefaultsAndValidate(string workerDirectory, ILogger logger);
3132
}
3233
}

src/WebJobs.Script/Workers/Rpc/Configuration/RpcWorkerConfigFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ internal void AddProvider(string workerDir)
140140
{
141141
workerDescription.DefaultWorkerPath = GetHydratedWorkerPath(workerDescription);
142142
}
143-
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
143+
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), _logger);
144144
_workerDescripionDictionary[workerDescription.Language] = workerDescription;
145145
}
146146
catch (Exception ex)

src/WebJobs.Script/Workers/Rpc/RpcWorkerDescription.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.IO;
88
using System.Linq;
99
using System.Runtime.InteropServices;
10+
using Microsoft.Extensions.Logging;
1011
using Newtonsoft.Json;
1112

1213
namespace Microsoft.Azure.WebJobs.Script.Workers.Rpc
@@ -65,7 +66,10 @@ public List<string> Extensions
6566
}
6667
}
6768

68-
public override void ApplyDefaultsAndValidate(string workerDirectory)
69+
// Can be replaced for testing purposes
70+
internal Func<string, bool> FileExists { private get; set; } = File.Exists;
71+
72+
public override void ApplyDefaultsAndValidate(string workerDirectory, ILogger logger)
6973
{
7074
if (workerDirectory == null)
7175
{
@@ -94,7 +98,7 @@ public override void ApplyDefaultsAndValidate(string workerDirectory)
9498
throw new FileNotFoundException($"Did not find {nameof(DefaultWorkerPath)} for language: {Language}");
9599
}
96100

97-
ResolveDotNetDefaultExecutablePath();
101+
ResolveDotNetDefaultExecutablePath(logger);
98102
}
99103

100104
public void ValidateWorkerPath(string workerPath, OSPlatform os, Architecture architecture, string version)
@@ -139,14 +143,29 @@ private void ValidateRuntimeVersion(string version)
139143
}
140144
}
141145

142-
private void ResolveDotNetDefaultExecutablePath()
146+
private void ResolveDotNetDefaultExecutablePath(ILogger logger)
143147
{
144148
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
145149
&& (DefaultExecutablePath.Equals(RpcWorkerConstants.DotNetExecutableName, StringComparison.OrdinalIgnoreCase)
146150
|| DefaultExecutablePath.Equals(RpcWorkerConstants.DotNetExecutableNameWithExtension, StringComparison.OrdinalIgnoreCase)))
147151
{
148152
var programFilesFolder = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
149-
DefaultExecutablePath = Path.Combine(programFilesFolder, RpcWorkerConstants.DotNetFolderName, DefaultExecutablePath);
153+
154+
var fullPath = Path.Combine(
155+
programFilesFolder,
156+
RpcWorkerConstants.DotNetFolderName,
157+
RpcWorkerConstants.DotNetExecutableNameWithExtension);
158+
159+
if (FileExists(fullPath))
160+
{
161+
DefaultExecutablePath = fullPath;
162+
}
163+
else
164+
{
165+
logger.Log(
166+
LogLevel.Warning,
167+
$"File '{fullPath}' is not found, '{DefaultExecutablePath}' invocation will rely on the PATH environment variable.");
168+
}
150169
}
151170
}
152171
}

test/WebJobs.Script.Tests/Workers/Rpc/RpcWorkerConfigTests.cs

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,17 +244,21 @@ public void ReadWorkerProviderFromConfig_OverrideDefaultExePath()
244244
[MemberData(nameof(InvalidWorkerDescriptions))]
245245
public void InvalidWorkerDescription_Throws(WorkerDescription workerDescription)
246246
{
247-
Assert.Throws<ValidationException>(() => workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory()));
247+
var testLogger = new TestLogger(testLanguage);
248+
249+
Assert.Throws<ValidationException>(() => workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger));
248250
}
249251

250252
[Theory]
251253
[MemberData(nameof(ValidWorkerDescriptions))]
252254
public void ValidateWorkerDescription_Succeeds(WorkerDescription workerDescription)
253255
{
256+
var testLogger = new TestLogger(testLanguage);
257+
254258
try
255259
{
256260
RpcWorkerConfigTestUtilities.CreateTestWorkerFileInCurrentDir();
257-
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
261+
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
258262
}
259263
finally
260264
{
@@ -267,26 +271,88 @@ public void ValidateWorkerDescription_Succeeds(WorkerDescription workerDescripti
267271
[InlineData("DotNet")]
268272
[InlineData("dotnet.exe")]
269273
[InlineData("DOTNET.EXE")]
270-
public void ValidateWorkerDescription_ResolvesDotNetDefaultWorkerExecutablePath(string defaultExecutablePath)
274+
public void ValidateWorkerDescription_ResolvesDotNetDefaultWorkerExecutablePath_WhenExpectedFileExists(
275+
string defaultExecutablePath)
271276
{
277+
var testLogger = new TestLogger(testLanguage);
278+
272279
var expectedExecutablePath =
273280
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
274-
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", defaultExecutablePath)
281+
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe")
275282
: defaultExecutablePath;
276283

277-
var workerDescription = new RpcWorkerDescription { Language = testLanguage, Extensions = new List<string>(), DefaultExecutablePath = defaultExecutablePath };
278-
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
284+
var workerDescription = new RpcWorkerDescription
285+
{
286+
Language = testLanguage,
287+
Extensions = new List<string>(),
288+
DefaultExecutablePath = defaultExecutablePath,
289+
FileExists = path =>
290+
{
291+
Assert.Equal(expectedExecutablePath, path);
292+
return true;
293+
}
294+
};
295+
296+
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
279297
Assert.Equal(expectedExecutablePath, workerDescription.DefaultExecutablePath);
280298
}
281299

300+
[Theory]
301+
[InlineData("dotnet")]
302+
[InlineData("DotNet")]
303+
[InlineData("dotnet.exe")]
304+
[InlineData("DOTNET.EXE")]
305+
public void ValidateWorkerDescription_DoesNotModifyDefaultWorkerExecutablePathAndWarns_WhenExpectedFileDoesNotExist(
306+
string defaultExecutablePath)
307+
{
308+
var testLogger = new TestLogger(testLanguage);
309+
310+
var expectedExecutablePath =
311+
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
312+
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe")
313+
: defaultExecutablePath;
314+
315+
var workerDescription = new RpcWorkerDescription
316+
{
317+
Language = testLanguage,
318+
Extensions = new List<string>(),
319+
DefaultExecutablePath = defaultExecutablePath,
320+
FileExists = path =>
321+
{
322+
Assert.Equal(expectedExecutablePath, path);
323+
return false;
324+
}
325+
};
326+
327+
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
328+
Assert.Equal(defaultExecutablePath, workerDescription.DefaultExecutablePath);
329+
Assert.True(testLogger.GetLogMessages().Any(message => message.Level == LogLevel.Warning
330+
&& message.FormattedMessage.Contains(defaultExecutablePath)
331+
&& message.FormattedMessage.Contains(expectedExecutablePath)));
332+
}
333+
282334
[Theory]
283335
[InlineData(@"D:\CustomExecutableFolder\dotnet")]
284336
[InlineData(@"/CustomExecutableFolder/dotnet")]
285337
[InlineData("AnythingElse")]
286-
public void ValidateWorkerDescription_DoesNotModifyDefaultWorkerExecutablePath_WhenDoesNotStrictlyMatchDotNet(string defaultExecutablePath)
338+
public void ValidateWorkerDescription_DoesNotModifyDefaultWorkerExecutablePath_WhenDoesNotStrictlyMatchDotNet(
339+
string defaultExecutablePath)
287340
{
288-
var workerDescription = new RpcWorkerDescription { Language = testLanguage, Extensions = new List<string>(), DefaultExecutablePath = defaultExecutablePath };
289-
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory());
341+
var testLogger = new TestLogger(testLanguage);
342+
343+
var workerDescription = new RpcWorkerDescription
344+
{
345+
Language = testLanguage,
346+
Extensions = new List<string>(),
347+
DefaultExecutablePath = defaultExecutablePath,
348+
FileExists = path =>
349+
{
350+
Assert.True(false, "FileExists should not be called");
351+
return false;
352+
}
353+
};
354+
355+
workerDescription.ApplyDefaultsAndValidate(Directory.GetCurrentDirectory(), testLogger);
290356
Assert.Equal(defaultExecutablePath, workerDescription.DefaultExecutablePath);
291357
}
292358

0 commit comments

Comments
 (0)