private void ValidateBundleRequirements(ExtensionBundleDetails bundleDetails) { if (_extensionRequirements.BundleRequirementsByBundleId.TryGetValue(bundleDetails.Id, out BundleRequirement requirement)) { var bundleVersion = new Version(bundleDetails.Version); var minimumVersion = new Version(requirement.MinimumVersion); if (bundleVersion < minimumVersion) { _logger.MinimumBundleVersionNotSatisfied(bundleDetails.Id, bundleDetails.Version, requirement.MinimumVersion); throw new HostInitializationException($"Referenced bundle {bundleDetails.Id} of version {bundleDetails.Version} does not meet the required minimum version of {requirement.MinimumVersion}. Update your extension bundle reference in host.json to reference {requirement.MinimumVersion} or later. For more information see https://aka.ms/func-min-bundle-versions."); } } }
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); }