Skip to content

Commit b316665

Browse files
committed
Fixing issue with indirect private assembly resolution
1 parent eb9a41e commit b316665

File tree

9 files changed

+208
-20
lines changed

9 files changed

+208
-20
lines changed

src/WebJobs.Script/Description/CSharp/CSharpFunctionInvoker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class CSharpFunctionInvoker : FunctionInvokerBase
3939
private readonly ReaderWriterLockSlim _functionValueLoaderLock = new ReaderWriterLockSlim();
4040

4141
private CSharpFunctionSignature _functionSignature;
42-
private FunctionMetadataResolver _metadataResolver;
42+
private IFunctionMetadataResolver _metadataResolver;
4343
private Action _reloadScript;
4444
private Action _restorePackages;
4545
private Action<MethodInfo, object[], object[], object> _resultProcessor;
@@ -317,7 +317,7 @@ private MethodInfo CreateFunctionTarget(CancellationToken cancellationToken)
317317
}
318318

319319
Assembly assembly = Assembly.Load(assemblyStream.GetBuffer(), pdbStream.GetBuffer());
320-
_assemblyLoader.CreateOrUpdateContext(Metadata, assembly, _metadataResolver);
320+
_assemblyLoader.CreateOrUpdateContext(Metadata, assembly, _metadataResolver, TraceWriter);
321321

322322
// Get our function entry point
323323
System.Reflection.TypeInfo scriptType = assembly.DefinedTypes.FirstOrDefault(t => string.Compare(t.Name, ScriptClassName, StringComparison.Ordinal) == 0);

src/WebJobs.Script/Description/CSharp/FunctionAssemblyLoadContext.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,28 @@
55
using System.Collections.Immutable;
66
using System.IO;
77
using System.Reflection;
8+
using Microsoft.Azure.WebJobs.Host;
89

910
namespace Microsoft.Azure.WebJobs.Script.Description
1011
{
1112
/// <summary>
1213
/// Establishes an assembly load context for a given function.
13-
/// Assemblies loaded from this context are loaded using the <see cref="FunctionMetadataResolver"/> associated
14+
/// Assemblies loaded from this context are loaded using the <see cref="IFunctionMetadataResolver"/> associated
1415
/// with a given function.
1516
/// </summary>
1617
[CLSCompliant(false)]
1718
public sealed class FunctionAssemblyLoadContext
1819
{
19-
private readonly FunctionMetadataResolver _metadataResolver;
20+
private readonly IFunctionMetadataResolver _metadataResolver;
21+
private readonly TraceWriter _traceWriter;
2022
private ImmutableArray<Assembly> _loadedAssemblies;
2123
private Uri _functionBaseUri;
2224

23-
public FunctionAssemblyLoadContext(FunctionMetadata functionMetadata, Assembly functionAssembly, FunctionMetadataResolver resolver)
25+
public FunctionAssemblyLoadContext(FunctionMetadata functionMetadata, Assembly functionAssembly, IFunctionMetadataResolver resolver, TraceWriter traceWriter)
2426
{
2527
_metadataResolver = resolver;
2628
_loadedAssemblies = ImmutableArray<Assembly>.Empty;
29+
_traceWriter = traceWriter;
2730
FunctionAssembly = functionAssembly;
2831
Metadata = functionMetadata;
2932
}
@@ -36,13 +39,21 @@ public ImmutableArray<Assembly> LoadedAssemblies
3639
}
3740
}
3841

42+
public TraceWriter TraceWriter
43+
{
44+
get
45+
{
46+
return _traceWriter;
47+
}
48+
}
49+
3950
public Uri FunctionBaseUri
4051
{
4152
get
4253
{
4354
if (_functionBaseUri == null)
4455
{
45-
_functionBaseUri = new Uri(Path.GetDirectoryName(Metadata.Source), UriKind.RelativeOrAbsolute);
56+
_functionBaseUri = new Uri(Path.GetDirectoryName(Metadata.Source) + "\\", UriKind.RelativeOrAbsolute);
4657
}
4758

4859
return _functionBaseUri;

src/WebJobs.Script/Description/CSharp/FunctionAssemblyLoader.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Reflection;
99
using System.Text.RegularExpressions;
10+
using Microsoft.Azure.WebJobs.Host;
1011

1112
namespace Microsoft.Azure.WebJobs.Script.Description
1213
{
@@ -45,21 +46,28 @@ private void Dispose(bool disposing)
4546
}
4647
}
4748

48-
private Assembly ResolveAssembly(object sender, ResolveEventArgs args)
49+
internal Assembly ResolveAssembly(object sender, ResolveEventArgs args)
4950
{
5051
FunctionAssemblyLoadContext context = GetFunctionContext(args.RequestingAssembly);
5152
Assembly result = null;
5253

5354
if (context != null)
5455
{
5556
result = context.ResolveAssembly(args.Name);
57+
58+
// Failed to resolve a function assembly dependency, log the failure as this
59+
// is usually caused by missing private assemblies.
60+
if (result == null)
61+
{
62+
context.TraceWriter.Warning(string.Format(CultureInfo.InvariantCulture, "Unable to find assembly '{0}'. Are you missing a private assembly file?", args.Name));
63+
}
5664
}
5765

5866
return result;
5967
}
6068

6169
[CLSCompliant(false)]
62-
public FunctionAssemblyLoadContext CreateOrUpdateContext(FunctionMetadata metadata, Assembly functionAssembly, FunctionMetadataResolver metadataResolver)
70+
public FunctionAssemblyLoadContext CreateOrUpdateContext(FunctionMetadata metadata, Assembly functionAssembly, IFunctionMetadataResolver metadataResolver, TraceWriter traceWriter)
6371
{
6472
if (metadata == null)
6573
{
@@ -73,8 +81,12 @@ public FunctionAssemblyLoadContext CreateOrUpdateContext(FunctionMetadata metada
7381
{
7482
throw new ArgumentNullException("metadataResolver");
7583
}
84+
if (traceWriter == null)
85+
{
86+
throw new ArgumentNullException("traceWriter");
87+
}
7688

77-
var context = new FunctionAssemblyLoadContext(metadata, functionAssembly, metadataResolver);
89+
var context = new FunctionAssemblyLoadContext(metadata, functionAssembly, metadataResolver, traceWriter);
7890

7991
return _functionContexts.AddOrUpdate(metadata.Name, context, (s, o) => context);
8092
}

src/WebJobs.Script/Description/CSharp/FunctionMetadataResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Microsoft.Azure.WebJobs.Script.Description
1919
/// or package assemblies.
2020
/// </summary>
2121
[CLSCompliant(false)]
22-
public sealed class FunctionMetadataResolver : MetadataReferenceResolver
22+
public sealed class FunctionMetadataResolver : MetadataReferenceResolver, IFunctionMetadataResolver
2323
{
2424
private readonly string _privateAssembliesPath;
2525
private readonly string[] _assemblyExtensions = new[] { ".exe", ".dll" };
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Collections.Immutable;
7+
using System.Reflection;
8+
using System.Threading.Tasks;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.Scripting;
11+
12+
namespace Microsoft.Azure.WebJobs.Script.Description
13+
{
14+
[CLSCompliant(false)]
15+
public interface IFunctionMetadataResolver
16+
{
17+
ScriptOptions FunctionScriptOptions { get; }
18+
19+
IReadOnlyCollection<string> GetCompilationReferences();
20+
21+
Assembly ResolveAssembly(string assemblyName);
22+
23+
ImmutableArray<PortableExecutableReference> ResolveReference(string reference, string baseFilePath, MetadataReferenceProperties properties);
24+
25+
Task RestorePackagesAsync();
26+
27+
bool TryGetPackageReference(string referenceName, out PackageReference package);
28+
}
29+
}

src/WebJobs.Script/WebJobs.Script.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@
261261
<Compile Include="Binding\QueueBinding.cs" />
262262
<Compile Include="Binding\ServiceBusBinding.cs" />
263263
<Compile Include="Binding\TableBinding.cs" />
264+
<Compile Include="Description\CSharp\IFunctionMetadataResolver.cs" />
264265
<Compile Include="Diagnostics\CompositeTraceWriter.cs" />
265266
<Compile Include="Config\JobHostConfigurationBuilder.cs" />
266267
<Compile Include="Config\AllowNameResolutionAttribute.cs" />
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Collections.Immutable;
7+
using System.Diagnostics;
8+
using System.Reflection;
9+
using System.Threading.Tasks;
10+
using Microsoft.Azure.WebJobs.Script.Description;
11+
using Microsoft.CodeAnalysis;
12+
using Moq;
13+
using Xunit;
14+
15+
namespace Microsoft.Azure.WebJobs.Script.Tests.Description.CSharp
16+
{
17+
public class FunctionAssemblyLoaderTests
18+
{
19+
[Fact]
20+
public void ResolveAssembly_WithIndirectPrivateDependency_IsResolved()
21+
{
22+
var resolver = new FunctionAssemblyLoader("c:\\");
23+
24+
var metadata1 = new FunctionMetadata { Name = "Test1", Source = @"c:\testroot\test1\test.tst" };
25+
var metadata2 = new FunctionMetadata { Name = "Test2", Source = @"c:\testroot\test2\test.tst" };
26+
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
27+
28+
var mockResolver = new Mock<IFunctionMetadataResolver>();
29+
mockResolver.Setup(m => m.ResolveAssembly("MyTestAssembly.dll"))
30+
.Returns(new TestAssembly(new AssemblyName("MyTestAssembly")));
31+
32+
resolver.CreateOrUpdateContext(metadata1, this.GetType().Assembly, new FunctionMetadataResolver(metadata1, traceWriter), traceWriter);
33+
resolver.CreateOrUpdateContext(metadata2, this.GetType().Assembly, mockResolver.Object, traceWriter);
34+
35+
Assembly result = resolver.ResolveAssembly(null, new System.ResolveEventArgs("MyTestAssembly.dll",
36+
new TestAssembly(new AssemblyName("MyDirectReference"), @"file:///c:/testroot/test2/bin/MyDirectReference.dll")));
37+
38+
Assert.NotNull(result);
39+
}
40+
41+
[Fact]
42+
public void ResolveAssembly_WithIndirectPrivateDependency_LogsIfResolutionFails()
43+
{
44+
var resolver = new FunctionAssemblyLoader("c:\\");
45+
46+
var metadata1 = new FunctionMetadata { Name = "Test1", Source = @"c:\testroot\test1\test.tst" };
47+
var metadata2 = new FunctionMetadata { Name = "Test2", Source = @"c:\testroot\test2\test.tst" };
48+
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
49+
50+
var mockResolver = new Mock<IFunctionMetadataResolver>();
51+
mockResolver.Setup(m => m.ResolveAssembly("MyTestAssembly.dll"))
52+
.Returns<Assembly>(null);
53+
54+
resolver.CreateOrUpdateContext(metadata1, this.GetType().Assembly, new FunctionMetadataResolver(metadata1, traceWriter), traceWriter);
55+
resolver.CreateOrUpdateContext(metadata2, this.GetType().Assembly, mockResolver.Object, traceWriter);
56+
57+
Assembly result = resolver.ResolveAssembly(null, new System.ResolveEventArgs("MyTestAssembly.dll",
58+
new TestAssembly(new AssemblyName("MyDirectReference"), @"file:///c:/testroot/test2/bin/MyDirectReference.dll")));
59+
60+
Assert.Null(result);
61+
Assert.Equal(1, traceWriter.Traces.Count);
62+
Assert.Contains("MyTestAssembly.dll", traceWriter.Traces[0].Message);
63+
}
64+
65+
private class TestAssembly : Assembly
66+
{
67+
private readonly AssemblyName _name;
68+
private readonly string _codebase;
69+
70+
public TestAssembly(AssemblyName name, string codebase = null)
71+
{
72+
_name = name;
73+
_codebase = codebase;
74+
}
75+
76+
public override string CodeBase
77+
{
78+
get
79+
{
80+
return _codebase;
81+
}
82+
}
83+
84+
public override string FullName
85+
{
86+
get
87+
{
88+
return _name.FullName;
89+
}
90+
}
91+
92+
public override AssemblyName GetName()
93+
{
94+
return _name;
95+
}
96+
}
97+
}
98+
}

test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@
108108
<HintPath>..\..\packages\Microsoft.CodeAnalysis.CSharp.1.1.1\lib\net45\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
109109
<Private>True</Private>
110110
</Reference>
111+
<Reference Include="Microsoft.CodeAnalysis.CSharp.Scripting, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
112+
<HintPath>..\..\packages\Microsoft.CodeAnalysis.CSharp.Scripting.1.1.1\lib\dotnet\Microsoft.CodeAnalysis.CSharp.Scripting.dll</HintPath>
113+
<Private>True</Private>
114+
</Reference>
115+
<Reference Include="Microsoft.CodeAnalysis.Scripting, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
116+
<HintPath>..\..\packages\Microsoft.CodeAnalysis.Scripting.Common.1.1.1\lib\dotnet\Microsoft.CodeAnalysis.Scripting.dll</HintPath>
117+
<Private>True</Private>
118+
</Reference>
111119
<Reference Include="Microsoft.Data.Edm, Version=5.6.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
112120
<HintPath>..\..\packages\Microsoft.Data.Edm.5.6.2\lib\net40\Microsoft.Data.Edm.dll</HintPath>
113121
<Private>True</Private>
@@ -165,12 +173,28 @@
165173
<Private>True</Private>
166174
</Reference>
167175
<Reference Include="System" />
176+
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
177+
<HintPath>..\..\packages\System.AppContext.4.0.0\lib\net46\System.AppContext.dll</HintPath>
178+
<Private>True</Private>
179+
</Reference>
168180
<Reference Include="System.Collections.Immutable, Version=1.1.37.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
169181
<HintPath>..\..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll</HintPath>
170182
<Private>True</Private>
171183
</Reference>
172184
<Reference Include="System.Core" />
173185
<Reference Include="System.Data" />
186+
<Reference Include="System.Diagnostics.StackTrace, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
187+
<HintPath>..\..\packages\System.Diagnostics.StackTrace.4.0.0\lib\net46\System.Diagnostics.StackTrace.dll</HintPath>
188+
<Private>True</Private>
189+
</Reference>
190+
<Reference Include="System.IO.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
191+
<HintPath>..\..\packages\System.IO.FileSystem.4.0.0\lib\net46\System.IO.FileSystem.dll</HintPath>
192+
<Private>True</Private>
193+
</Reference>
194+
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
195+
<HintPath>..\..\packages\System.IO.FileSystem.Primitives.4.0.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
196+
<Private>True</Private>
197+
</Reference>
174198
<Reference Include="System.Net.Http" />
175199
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
176200
<HintPath>..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
@@ -224,6 +248,7 @@
224248
<Compile Include="CompositeTraceWriterTests.cs" />
225249
<Compile Include="CSharpEndToEndTests.cs" />
226250
<Compile Include="CSharpFunctionSignatureTests.cs" />
251+
<Compile Include="Description\CSharp\FunctionAssemblyLoaderTests.cs" />
227252
<Compile Include="EndToEndTestFixture.cs" />
228253
<Compile Include="EndToEndTestsBase.cs" />
229254
<Compile Include="FileTraceWriterTests.cs" />
@@ -253,7 +278,9 @@
253278
<None Include="app.config">
254279
<SubType>Designer</SubType>
255280
</None>
256-
<None Include="packages.config" />
281+
<None Include="packages.config">
282+
<SubType>Designer</SubType>
283+
</None>
257284
</ItemGroup>
258285
<ItemGroup>
259286
<None Include="TestScripts\Bash\host.json">

test/WebJobs.Script.Tests/packages.config

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net46" />
2525
<package id="Microsoft.CodeAnalysis.Common" version="1.1.1" targetFramework="net46" />
2626
<package id="Microsoft.CodeAnalysis.CSharp" version="1.1.1" targetFramework="net46" />
27+
<package id="Microsoft.CodeAnalysis.CSharp.Scripting" version="1.1.1" targetFramework="net46" />
28+
<package id="Microsoft.CodeAnalysis.Scripting.Common" version="1.1.1" targetFramework="net46" />
2729
<package id="Microsoft.Data.Edm" version="5.6.2" targetFramework="net45" />
2830
<package id="Microsoft.Data.OData" version="5.6.2" targetFramework="net45" />
2931
<package id="Microsoft.Data.Services.Client" version="5.6.2" targetFramework="net45" />
@@ -36,24 +38,32 @@
3638
<package id="Sendgrid" version="6.3.4" targetFramework="net46" />
3739
<package id="SendGrid.SmtpApi" version="1.3.1" targetFramework="net45" />
3840
<package id="StyleCop.MSBuild" version="4.7.50.0" targetFramework="net46" developmentDependency="true" />
39-
<package id="System.Collections" version="4.0.0" targetFramework="net46" />
41+
<package id="System.AppContext" version="4.0.0" targetFramework="net46" />
42+
<package id="System.Collections" version="4.0.10" targetFramework="net46" />
4043
<package id="System.Collections.Immutable" version="1.1.37" targetFramework="net46" />
41-
<package id="System.Diagnostics.Debug" version="4.0.0" targetFramework="net46" />
42-
<package id="System.Globalization" version="4.0.0" targetFramework="net46" />
43-
<package id="System.IO" version="4.0.0" targetFramework="net46" />
44+
<package id="System.Diagnostics.Debug" version="4.0.10" targetFramework="net46" />
45+
<package id="System.Diagnostics.StackTrace" version="4.0.0" targetFramework="net46" />
46+
<package id="System.Diagnostics.Tools" version="4.0.0" targetFramework="net46" />
47+
<package id="System.Globalization" version="4.0.10" targetFramework="net46" />
48+
<package id="System.IO" version="4.0.10" targetFramework="net46" />
49+
<package id="System.IO.FileSystem" version="4.0.0" targetFramework="net46" />
50+
<package id="System.IO.FileSystem.Primitives" version="4.0.0" targetFramework="net46" />
4451
<package id="System.Linq" version="4.0.0" targetFramework="net46" />
45-
<package id="System.Reflection" version="4.0.0" targetFramework="net46" />
52+
<package id="System.Linq.Expressions" version="4.0.10" targetFramework="net46" />
53+
<package id="System.Reflection" version="4.0.10" targetFramework="net46" />
4654
<package id="System.Reflection.Extensions" version="4.0.0" targetFramework="net46" />
4755
<package id="System.Reflection.Metadata" version="1.1.0" targetFramework="net46" />
4856
<package id="System.Reflection.Primitives" version="4.0.0" targetFramework="net46" />
4957
<package id="System.Resources.ResourceManager" version="4.0.0" targetFramework="net46" />
50-
<package id="System.Runtime" version="4.0.0" targetFramework="net46" />
51-
<package id="System.Runtime.Extensions" version="4.0.0" targetFramework="net46" />
52-
<package id="System.Runtime.InteropServices" version="4.0.0" targetFramework="net46" />
58+
<package id="System.Runtime" version="4.0.20" targetFramework="net46" />
59+
<package id="System.Runtime.Extensions" version="4.0.10" targetFramework="net46" />
60+
<package id="System.Runtime.Handles" version="4.0.0" targetFramework="net46" />
61+
<package id="System.Runtime.InteropServices" version="4.0.20" targetFramework="net46" />
5362
<package id="System.Spatial" version="5.6.2" targetFramework="net45" />
5463
<package id="System.Text.Encoding" version="4.0.0" targetFramework="net46" />
5564
<package id="System.Text.Encoding.Extensions" version="4.0.0" targetFramework="net46" />
56-
<package id="System.Threading" version="4.0.0" targetFramework="net46" />
65+
<package id="System.Threading" version="4.0.10" targetFramework="net46" />
66+
<package id="System.Threading.Tasks" version="4.0.10" targetFramework="net46" />
5767
<package id="WindowsAzure.ServiceBus" version="2.7.6" targetFramework="net45" />
5868
<package id="WindowsAzure.Storage" version="4.3.0" targetFramework="net45" />
5969
<package id="xunit" version="2.0.0" targetFramework="net45" />

0 commit comments

Comments
 (0)