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();
        }
Esempio n. 3
0
        [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));
        }
Esempio n. 4
0
        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);
        }
Esempio n. 6
0
        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);
        }
Esempio n. 7
0
        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);
        }