public void DefaultContextAssembly_LoadedIntoSharedContext_UsesRuntimeAssembly() { using (var tempFolder = new TempDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()))) using (var env = new TestScopedEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsScriptRoot, tempFolder.Path)) { // Arrange var sharedContext = new FunctionAssemblyLoadContext(tempFolder.Path); Assembly targetAssembly = typeof(DynamicFunctionAssemblyLoadContextTests).Assembly; string originalLocation = targetAssembly.Location; string targetAssemblyLocation = Path.Combine(tempFolder.Path, Path.GetFileName(originalLocation)); File.Copy(originalLocation, targetAssemblyLocation); var metadata1Directory = @"c:\testroot\test1"; var metadata1 = new WebJobs.Script.Description.FunctionMetadata { Name = "Test1", ScriptFile = $@"{metadata1Directory}\test.tst" }; var mockResolver = new Mock <IFunctionMetadataResolver>(); var loadContext = new TestDynamicAssemblyLoadContext(metadata1, mockResolver.Object, NullLogger.Instance, sharedContext); // Act Assembly result = loadContext.LoadFromAssemblyName(targetAssembly.GetName()); // Assert Assert.NotNull(result); Assert.Same(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(result)); } }
public async Task SpecializeHostCoreAsync() { // Go async immediately to ensure that any async context from // the PlaceholderSpecializationMiddleware is properly suppressed. await Task.Yield(); _logger.LogInformation(Resources.HostSpecializationTrace); // After specialization, we need to ensure that custom timezone // settings configured by the user (WEBSITE_TIME_ZONE) are honored. // DateTime caches timezone information, so we need to clear the cache. TimeZoneInfo.ClearCachedData(); // Trigger a configuration reload to pick up all current settings _configuration?.Reload(); _hostNameProvider.Reset(); // Reset the shared load context to ensure we're reloading // user dependencies FunctionAssemblyLoadContext.ResetSharedContext(); await _languageWorkerChannelManager.SpecializeAsync(); NotifyChange(); await _scriptHostManager.RestartHostAsync(); await _scriptHostManager.DelayUntilHostReady(); }
[InlineData("System.IO")] // System.* public void RuntimeAssemblies_AreLoadedInDefaultContext(string assemblyName) { var functionContext = new FunctionAssemblyLoadContext(AppContext.BaseDirectory); var assembly = functionContext.LoadFromAssemblyName(new AssemblyName(assemblyName)); Assert.NotNull(assembly); Assert.NotSame(functionContext, AssemblyLoadContext.GetLoadContext(assembly)); Assert.Same(AssemblyLoadContext.Default, AssemblyLoadContext.GetLoadContext(assembly)); }
public void InitializeDeps_LoadsExpectedDependencies() { string depsPath = Path.Combine(Directory.GetCurrentDirectory(), "Description", "DotNet", "TestFiles", "DepsFiles"); List <string> currentRidFallbacks = DependencyHelper.GetRuntimeFallbacks(); (IDictionary <string, RuntimeAsset[]> depsAssemblies, IDictionary <string, RuntimeAsset[]> nativeLibraries) = FunctionAssemblyLoadContext.InitializeDeps(depsPath, currentRidFallbacks); string testRid = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win" : "unix"; // Ensure runtime specific dependencies are resolved, with appropriate RID FunctionAssemblyLoadContext.TryGetDepsAsset(depsAssemblies, "System.private.servicemodel", currentRidFallbacks, out string assemblyPath); Assert.Equal($"runtimes/{testRid}/lib/netstandard2.0/System.Private.ServiceModel.dll", assemblyPath); FunctionAssemblyLoadContext.TryGetDepsAsset(depsAssemblies, "System.text.encoding.codepages", currentRidFallbacks, out assemblyPath); Assert.Equal($"runtimes/{testRid}/lib/netstandard1.3/System.Text.Encoding.CodePages.dll", assemblyPath); // Ensure flattened dependency has expected path FunctionAssemblyLoadContext.TryGetDepsAsset(depsAssemblies, "Microsoft.Azure.WebJobs.Host.Storage", currentRidFallbacks, out assemblyPath); Assert.Equal($"Microsoft.Azure.WebJobs.Host.Storage.dll", assemblyPath); // Ensure native libraries are resolved, with appropriate RID and path string nativeRid; string nativePrefix; string nativeExtension; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { nativeRid = "win-"; nativePrefix = string.Empty; nativeExtension = "dll"; } else { nativePrefix = "lib"; if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { nativeRid = "osx-"; nativeExtension = "dylib"; } else { nativeRid = "linux-"; nativeExtension = "so"; } } nativeRid += Environment.Is64BitProcess ? "x64" : "x86"; string nativeAssetFileName = $"{nativePrefix}CpuMathNative.{nativeExtension}"; FunctionAssemblyLoadContext.TryGetDepsAsset(nativeLibraries, nativeAssetFileName, currentRidFallbacks, out string assetPath); Assert.Contains($"runtimes/{nativeRid}/nativeassets/netstandard2.0/{nativeAssetFileName}", assetPath); }
public void InitializeDeps_LoadsExpectedDependencies() { string depsPath = Path.Combine(Directory.GetCurrentDirectory(), "Description", "DotNet", "TestFiles", "DepsFiles"); IDictionary <string, string> assemblies = FunctionAssemblyLoadContext.InitializeDeps(depsPath); string testRid = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win" : "unix"; // Ensure runtime specific dependencies are resolved, with appropriate RID Assert.Contains($"runtimes/{testRid}/lib/netstandard2.0/System.Private.ServiceModel.dll", assemblies.Values); Assert.Contains($"runtimes/{testRid}/lib/netstandard1.3/System.Text.Encoding.CodePages.dll", assemblies.Values); // Ensure flattened dependency has expected path Assert.Contains($"Microsoft.Azure.WebJobs.Host.Storage.dll", assemblies.Values); }
public void ProbeForNativeAssets_FindsAsset(string assetPath) { var probingPaths = new List <string> { @"c:\a\bin" }; Mock <FileBase> mockFile = new Mock <FileBase>(MockBehavior.Strict); mockFile .Setup(m => m.Exists(It.IsAny <string>())) .Returns <string>(s => s == assetPath); string result = FunctionAssemblyLoadContext.ProbeForNativeAsset(probingPaths, "assembly.dll", mockFile.Object); Assert.Equal(assetPath, result); }
public void InitializeDeps_WithRidSpecificNativeAssets_LoadsExpectedDependencies(string rid, string expectedNativeRid, string prefix, string suffix, bool expectMatch) { string depsPath = Path.Combine(Directory.GetCurrentDirectory(), "Description", "DotNet", "TestFiles", "DepsFiles", "RidNativeDeps"); List <string> ridFallback = DependencyHelper.GetRuntimeFallbacks(rid); (_, IDictionary <string, RuntimeAsset[]> nativeLibraries) = FunctionAssemblyLoadContext.InitializeDeps(depsPath, ridFallback); string nativeAssetFileName = $"{prefix}Cosmos.CRTCompat.{suffix}"; FunctionAssemblyLoadContext.TryGetDepsAsset(nativeLibraries, nativeAssetFileName, ridFallback, out string assetPath); string expectedMatch = expectMatch ? $"runtimes/{expectedNativeRid}/native/{nativeAssetFileName}" : null; Assert.Equal(expectedMatch, assetPath); }
public async Task <IEnumerable <Type> > GetExtensionsStartupTypesAsync() { string extensionsPath; FunctionAssemblyLoadContext.ResetSharedContext(); var functionMetadataCollection = _functionMetadataManager.GetFunctionMetadata(forceRefresh: true, includeCustomProviders: false); HashSet <string> bindingsSet = null; var bundleConfigured = _extensionBundleManager.IsExtensionBundleConfigured(); bool isPrecompiledFunctionApp = false; if (bundleConfigured) { bindingsSet = new HashSet <string>(StringComparer.OrdinalIgnoreCase); // Generate a Hashset of all the binding types used in the function app foreach (var functionMetadata in functionMetadataCollection) { foreach (var binding in functionMetadata.Bindings) { bindingsSet.Add(binding.Type); } isPrecompiledFunctionApp = isPrecompiledFunctionApp || functionMetadata.Language == DotNetScriptTypes.DotNetAssembly; } } bool isLegacyExtensionBundle = _extensionBundleManager.IsLegacyExtensionBundle(); if (bundleConfigured && (!isPrecompiledFunctionApp || _extensionBundleManager.IsLegacyExtensionBundle())) { extensionsPath = await _extensionBundleManager.GetExtensionBundleBinPathAsync(); if (string.IsNullOrEmpty(extensionsPath)) { _logger.ScriptStartUpErrorLoadingExtensionBundle(); return(new Type[0]); } _logger.ScriptStartUpLoadingExtensionBundle(extensionsPath); } else { extensionsPath = Path.Combine(_rootScriptPath, "bin"); if (!File.Exists(Path.Combine(extensionsPath, ScriptConstants.ExtensionsMetadataFileName)) && File.Exists(Path.Combine(_rootScriptPath, ScriptConstants.ExtensionsMetadataFileName))) { // As a fallback, allow extensions.json in the root path. extensionsPath = _rootScriptPath; } _logger.ScriptStartNotLoadingExtensionBundle(extensionsPath, bundleConfigured, isPrecompiledFunctionApp, isLegacyExtensionBundle); } string metadataFilePath = Path.Combine(extensionsPath, ScriptConstants.ExtensionsMetadataFileName); // parse the extensions file to get declared startup extensions ExtensionReference[] extensionItems = ParseExtensions(metadataFilePath); var startupTypes = new List <Type>(); foreach (var extensionItem in extensionItems) { if (!bundleConfigured || extensionItem.Bindings.Count == 0 || extensionItem.Bindings.Intersect(bindingsSet, StringComparer.OrdinalIgnoreCase).Any()) { string startupExtensionName = extensionItem.Name ?? extensionItem.TypeName; _logger.ScriptStartUpLoadingStartUpExtension(startupExtensionName); // load the Type for each startup extension into the function assembly load context Type extensionType = Type.GetType(extensionItem.TypeName, assemblyName => { if (_builtinExtensionAssemblies.Contains(assemblyName.Name, StringComparer.OrdinalIgnoreCase)) { _logger.ScriptStartUpBelongExtension(extensionItem.TypeName); return(null); } string path = extensionItem.HintPath; if (string.IsNullOrEmpty(path)) { path = assemblyName.Name + ".dll"; } var hintUri = new Uri(path, UriKind.RelativeOrAbsolute); if (!hintUri.IsAbsoluteUri) { path = Path.Combine(extensionsPath, path); } if (File.Exists(path)) { return(FunctionAssemblyLoadContext.Shared.LoadFromAssemblyPath(path, true)); } return(null); }, (assembly, typeName, ignoreCase) => { _logger.ScriptStartUpLoadedExtension(startupExtensionName, assembly.GetName().Version.ToString()); return(assembly?.GetType(typeName, false, ignoreCase)); }, false, true); if (extensionType == null) { _logger.ScriptStartUpUnableToLoadExtension(startupExtensionName, extensionItem.TypeName); continue; } if (!typeof(IWebJobsStartup).IsAssignableFrom(extensionType) && !typeof(IWebJobsConfigurationStartup).IsAssignableFrom(extensionType)) { _logger.ScriptStartUpTypeIsNotValid(extensionItem.TypeName, nameof(IWebJobsStartup), nameof(IWebJobsConfigurationStartup)); continue; } startupTypes.Add(extensionType); } } return(startupTypes); }
public TestDynamicAssemblyLoadContext(WebJobs.Script.Description.FunctionMetadata functionMetadata, IFunctionMetadataResolver resolver, ILogger logger, FunctionAssemblyLoadContext sharedContext) : base(functionMetadata, resolver, logger) { _sharedContext = sharedContext; }
public async Task <IEnumerable <Type> > GetExtensionsStartupTypesAsync() { string extensionsMetadataPath; FunctionAssemblyLoadContext.ResetSharedContext(); HashSet <string> bindingsSet = null; var bundleConfigured = _extensionBundleManager.IsExtensionBundleConfigured(); bool isLegacyExtensionBundle = _extensionBundleManager.IsLegacyExtensionBundle(); bool isPrecompiledFunctionApp = false; // if workerIndexing // Function.json (httpTrigger, blobTrigger, blobTrigger) -> httpTrigger, blobTrigger // dotnet app precompiled -> Do not use bundles var workerConfigs = _languageWorkerOptions.Value.WorkerConfigs; if (bundleConfigured && !Utility.CanWorkerIndex(workerConfigs, SystemEnvironment.Instance)) { ExtensionBundleDetails bundleDetails = await _extensionBundleManager.GetExtensionBundleDetails(); ValidateBundleRequirements(bundleDetails); var functionMetadataCollection = _functionMetadataManager.GetFunctionMetadata(forceRefresh: true, includeCustomProviders: false); bindingsSet = new HashSet <string>(StringComparer.OrdinalIgnoreCase); // Generate a Hashset of all the binding types used in the function app foreach (var functionMetadata in functionMetadataCollection) { foreach (var binding in functionMetadata.Bindings) { bindingsSet.Add(binding.Type); } isPrecompiledFunctionApp = isPrecompiledFunctionApp || functionMetadata.Language == DotNetScriptTypes.DotNetAssembly; } } if (SystemEnvironment.Instance.IsPlaceholderModeEnabled()) { // Do not move this. // Calling this log statement in the placeholder mode to avoid jitting during specializtion _logger.ScriptStartNotLoadingExtensionBundle("WARMUP_LOG_ONLY", bundleConfigured, isPrecompiledFunctionApp, isLegacyExtensionBundle); } string baseProbingPath = null; if (bundleConfigured && (!isPrecompiledFunctionApp || isLegacyExtensionBundle)) { extensionsMetadataPath = await _extensionBundleManager.GetExtensionBundleBinPathAsync(); if (string.IsNullOrEmpty(extensionsMetadataPath)) { _logger.ScriptStartUpErrorLoadingExtensionBundle(); return(Array.Empty <Type>()); } _logger.ScriptStartUpLoadingExtensionBundle(extensionsMetadataPath); } else { extensionsMetadataPath = Path.Combine(_rootScriptPath, "bin"); // Verify if the file exists and apply fallback paths // The fallback order is: // 1 - Script root // - If the system folder exists with metadata file at the root, use that as the base probing path // 2 - System folder if (!File.Exists(Path.Combine(extensionsMetadataPath, ScriptConstants.ExtensionsMetadataFileName))) { string systemPath = Path.Combine(_rootScriptPath, ScriptConstants.AzureFunctionsSystemDirectoryName); if (File.Exists(Path.Combine(_rootScriptPath, ScriptConstants.ExtensionsMetadataFileName))) { // As a fallback, allow extensions.json in the root path. extensionsMetadataPath = _rootScriptPath; // If the system path exists, that should take precedence as the base probing path if (Directory.Exists(systemPath)) { baseProbingPath = systemPath; } } else if (File.Exists(Path.Combine(systemPath, ScriptConstants.ExtensionsMetadataFileName))) { extensionsMetadataPath = systemPath; } } _logger.ScriptStartNotLoadingExtensionBundle(extensionsMetadataPath, bundleConfigured, isPrecompiledFunctionApp, isLegacyExtensionBundle); } baseProbingPath ??= extensionsMetadataPath; _logger.ScriptStartupResettingLoadContextWithBasePath(baseProbingPath); // Reset the load context using the resolved extensions path FunctionAssemblyLoadContext.ResetSharedContext(baseProbingPath); string metadataFilePath = Path.Combine(extensionsMetadataPath, ScriptConstants.ExtensionsMetadataFileName); // parse the extensions file to get declared startup extensions ExtensionReference[] extensionItems = ParseExtensions(metadataFilePath); var startupTypes = new List <Type>(); foreach (var extensionItem in extensionItems) { // We need to explicitly ignore ApplicationInsights extension if (extensionItem.TypeName.Equals(ApplicationInsightsStartupType, StringComparison.Ordinal)) { _logger.LogWarning("The Application Insights extension is no longer supported. Package references to Microsoft.Azure.WebJobs.Extensions.ApplicationInsights can be removed."); continue; } if (Utility.CanWorkerIndex(workerConfigs, SystemEnvironment.Instance) || !bundleConfigured || extensionItem.Bindings.Count == 0 || extensionItem.Bindings.Intersect(bindingsSet, StringComparer.OrdinalIgnoreCase).Any()) { string startupExtensionName = extensionItem.Name ?? extensionItem.TypeName; _logger.ScriptStartUpLoadingStartUpExtension(startupExtensionName); // load the Type for each startup extension into the function assembly load context Type extensionType = Type.GetType(extensionItem.TypeName, assemblyName => { if (_builtinExtensionAssemblies.Contains(assemblyName.Name, StringComparer.OrdinalIgnoreCase)) { _logger.ScriptStartUpBelongExtension(extensionItem.TypeName); return(null); } string path = extensionItem.HintPath; if (string.IsNullOrEmpty(path)) { path = assemblyName.Name + ".dll"; } var hintUri = new Uri(path, UriKind.RelativeOrAbsolute); if (!hintUri.IsAbsoluteUri) { path = Path.Combine(extensionsMetadataPath, path); } if (File.Exists(path)) { return(FunctionAssemblyLoadContext.Shared.LoadFromAssemblyPath(path, true)); } return(null); }, (assembly, typeName, ignoreCase) => { _logger.ScriptStartUpLoadedExtension(startupExtensionName, assembly.GetName().Version.ToString()); return(assembly?.GetType(typeName, false, ignoreCase)); }, false, true); if (extensionType == null) { _logger.ScriptStartUpUnableToLoadExtension(startupExtensionName, extensionItem.TypeName); continue; } if (!typeof(IWebJobsStartup).IsAssignableFrom(extensionType) && !typeof(IWebJobsConfigurationStartup).IsAssignableFrom(extensionType)) { _logger.ScriptStartUpTypeIsNotValid(extensionItem.TypeName, nameof(IWebJobsStartup), nameof(IWebJobsConfigurationStartup)); continue; } startupTypes.Add(extensionType); } } ValidateExtensionRequirements(startupTypes); return(startupTypes); }
public void GetUnmanagedLibraryFileNames_ReturnsExpectedResults(string libName, string[] expectedResults, OSPlatform platform) { var result = FunctionAssemblyLoadContext.GetUnmanagedLibraryFileNames(libName, platform); Assert.Equal(expectedResults, result); }