internal void TraceConfiguration(EndToEndTraceHelper traceHelper, JObject storageProviderConfig) { // Clone the options to avoid making changes to the original. // We make updates to the clone rather than to JSON to ensure we're updating what we think we're updating. DurableTaskOptions clone = JObject.FromObject(this).ToObject <DurableTaskOptions>(); // At this stage the task hub name is expected to have been resolved. However, we want to know // what the original value was in addition to the resolved value, so we're updating the JSON // blob property to use the original, unresolved value. clone.HubName = this.originalHubName; // Format the options data as JSON in a way that is friendly for technical humans to read. JObject configurationJson = JObject.FromObject( clone, new JsonSerializer { Converters = { new StringEnumConverter() }, ContractResolver = new CamelCasePropertyNamesContractResolver(), }); if (storageProviderConfig.Count != 0) { configurationJson["storageProvider"] = storageProviderConfig; } // This won't be exactly the same as what is declared in host.json because any unspecified values // will have been initialized with their defaults. We need the Functions runtime to handle tracing // of the actual host.json values: https://github.com/Azure/azure-functions-host/issues/5422. traceHelper.TraceConfiguration(this.HubName, configurationJson.ToString(Formatting.None)); }
internal void TraceConfiguration(EndToEndTraceHelper traceHelper) { // Clone the options to avoid making changes to the original. // We make updates to the clone rather than to JSON to ensure we're updating what we think we're updating. DurableTaskOptions clone = JObject.FromObject(this).ToObject <DurableTaskOptions>(); // Don't trace the notification URL query string since it may contain secrets. // This is the only property which we expect to contain secrets. Everything else should be *names* // of secrets that are resolved later from environment variables, etc. if (clone.NotificationUrl != null) { clone.NotificationUrl = new Uri(clone.NotificationUrl.GetLeftPart(UriPartial.Path)); } // At this stage the task hub name is expected to have been resolved. However, we want to know // what the original value was in addition to the resolved value, so we're updating the JSON // blob property to use the original, unresolved value. clone.HubName = this.originalHubName; // Format the options data as JSON in a way that is friendly for technical humans to read. JObject configurationJson = JObject.FromObject( clone, new JsonSerializer { Converters = { new StringEnumConverter() }, ContractResolver = new CamelCasePropertyNamesContractResolver(), }); // This won't be exactly the same as what is declared in host.json because any unspecified values // will have been initialized with their defaults. We need the Functions runtime to handle tracing // of the actual host.json values: https://github.com/Azure/azure-functions-host/issues/5422. traceHelper.TraceConfiguration(this.HubName, configurationJson.ToString(Formatting.None)); }
/// <summary> /// Create an EventSourceListener to capture and log Durable EventSource /// data in Linux. /// </summary> /// <param name="logger">A LinuxAppService logger configured for the current linux host.</param> /// <param name="enableVerbose">If true, durableTask.Core verbose logs are enabled. The opposite if false.</param> /// <param name="traceHelper">A tracing client to log exceptions.</param> /// <param name="durabilityProviderEventSourceName">The durability provider's event source name.</param> public EventSourceListener(LinuxAppServiceLogger logger, bool enableVerbose, EndToEndTraceHelper traceHelper, string durabilityProviderEventSourceName) { this.logger = logger; this.disableVerbose = !enableVerbose; // We track the opposite value ro simplify logic later this.traceHelper = traceHelper; this.durabilityProviderEventSourceName = durabilityProviderEventSourceName; }
void IExtensionConfigProvider.Initialize(ExtensionConfigContext context) { ConfigureLoaderHooks(); context.ApplyConfig(this, "DurableTask"); // Register the trigger bindings JobHostConfiguration hostConfig = context.Config; this.traceHelper = new EndToEndTraceHelper(context.Trace); this.httpApiHandler = new HttpApiHandler(this, context.Trace); // Register the non-trigger bindings, which have a different model. var bindings = new BindingHelper(this, this.traceHelper); // For 202 support if (this.NotificationUrl == null) { this.NotificationUrl = context.GetWebhookHandler(); } // Note that the order of the rules is important var rule = context.AddBindingRule <OrchestrationClientAttribute>() .AddConverter <JObject, StartOrchestrationArgs>(bindings.JObjectToStartOrchestrationArgs); rule.BindToCollector <StartOrchestrationArgs>(bindings.CreateAsyncCollector); rule.BindToInput <DurableOrchestrationClient>(GetClient); context.AddBindingRule <OrchestrationTriggerAttribute>() .BindToTrigger(new OrchestrationTriggerAttributeBindingProvider(this, context, this.traceHelper)); context.AddBindingRule <ActivityTriggerAttribute>() .BindToTrigger(new ActivityTriggerAttributeBindingProvider(this, context, this.traceHelper)); }
public ActivityTriggerAttributeBindingProvider( DurableTaskExtension durableTaskConfig, ExtensionConfigContext extensionContext, EndToEndTraceHelper traceHelper) { this.durableTaskConfig = durableTaskConfig; this.extensionContext = extensionContext; this.traceHelper = traceHelper; }
public OrchestrationTriggerAttributeBindingProvider( DurableTaskExtension config, ExtensionConfigContext extensionContext, EndToEndTraceHelper traceHelper) { this.config = config; this.extensionContext = extensionContext; this.traceHelper = traceHelper; }
public EntityTriggerAttributeBindingProvider( DurableTaskExtension config, ExtensionConfigContext extensionContext, string storageConnectionString, EndToEndTraceHelper traceHelper) { this.config = config; this.extensionContext = extensionContext; this.storageConnectionString = storageConnectionString; this.traceHelper = traceHelper; }
public ActivityTriggerAttributeBindingProvider( DurableTaskConfiguration durableTaskConfig, ExtensionConfigContext extensionContext, EndToEndTraceHelper traceHelper) { this.durableTaskConfig = durableTaskConfig; this.extensionContext = extensionContext; this.traceHelper = traceHelper; ActivityTriggerBinding.RegisterBindingRules(extensionContext.Config); }
public LifeCycleNotificationHelper( DurableTaskOptions config, INameResolver nameResolver, EndToEndTraceHelper traceHelper) { this.config = config ?? throw new ArgumentNullException(nameof(config)); this.traceHelper = traceHelper ?? throw new ArgumentNullException(nameof(traceHelper)); if (nameResolver == null) { throw new ArgumentNullException(nameof(nameResolver)); } this.eventGridKeyValue = nameResolver.Resolve(config.EventGridKeySettingName); this.eventGridTopicEndpoint = config.EventGridTopicEndpoint; if (nameResolver.TryResolveWholeString(config.EventGridTopicEndpoint, out var endpoint)) { this.eventGridTopicEndpoint = endpoint; } if (!string.IsNullOrEmpty(this.eventGridTopicEndpoint)) { if (!string.IsNullOrEmpty(config.EventGridKeySettingName)) { this.useTrace = true; var retryStatusCode = config.EventGridPublishRetryHttpStatus? .Where(x => Enum.IsDefined(typeof(HttpStatusCode), x)) .Select(x => (HttpStatusCode)x) .ToArray() ?? Array.Empty <HttpStatusCode>(); // Currently, we support Event Grid Custom Topic for notify the lifecycle event of an orchestrator. // For more detail about the Event Grid, please refer this document. // Post to custom topic for Azure Event Grid // https://docs.microsoft.com/en-us/azure/event-grid/post-to-custom-topic this.HttpMessageHandler = config.NotificationHandler ?? new HttpRetryMessageHandler( new HttpClientHandler(), config.EventGridPublishRetryCount, config.EventGridPublishRetryInterval, retryStatusCode); if (string.IsNullOrEmpty(this.eventGridKeyValue)) { throw new ArgumentException($"Failed to start lifecycle notification feature. Please check the configuration values for {config.EventGridKeySettingName} on AppSettings."); } } else { throw new ArgumentException($"Failed to start lifecycle notification feature. Please check the configuration values for {config.EventGridTopicEndpoint} and {config.EventGridKeySettingName}."); } } }
public LocalHttpListener( EndToEndTraceHelper traceHelper, DurableTaskOptions durableTaskOptions, Func <HttpRequestMessage, Task <HttpResponseMessage> > handler) { this.traceHelper = traceHelper ?? throw new ArgumentNullException(nameof(traceHelper)); this.handler = handler ?? throw new ArgumentNullException(nameof(handler)); this.durableTaskOptions = durableTaskOptions ?? throw new ArgumentNullException(nameof(durableTaskOptions)); // Set to a non null value this.InternalRpcUri = new Uri($"http://uninitialized"); this.localWebHost = new NoOpWebHost(); }
private void InitializeForFunctionsV1(ExtensionConfigContext context) { #if !NETSTANDARD2_0 context.ApplyConfig(this.Options, "DurableTask"); ILogger logger = context.Config.LoggerFactory.CreateLogger(LoggerCategoryName); this.TraceHelper = new EndToEndTraceHelper(logger, this.Options.LogReplayEvents); this.HttpApiHandler = new HttpApiHandler(this, logger); this.connectionStringResolver = new WebJobsConnectionStringProvider(); this.LifeCycleNotificationHelper = this.CreateLifeCycleNotificationHelper(); this.nameResolver = context.Config.NameResolver; #endif }
private void InitializeForFunctionsV1(ExtensionConfigContext context) { #if FUNCTIONS_V1 context.ApplyConfig(this.Options, "DurableTask"); ILogger logger = context.Config.LoggerFactory.CreateLogger(LoggerCategoryName); this.TraceHelper = new EndToEndTraceHelper(logger, this.Options.Tracing.TraceReplayEvents); this.connectionStringResolver = new WebJobsConnectionStringProvider(); this.durabilityProviderFactory = new AzureStorageDurabilityProviderFactory(new OptionsWrapper <DurableTaskOptions>(this.Options), this.connectionStringResolver); this.defaultDurabilityProvider = this.durabilityProviderFactory.GetDurabilityProvider(); this.nameResolver = context.Config.NameResolver; this.LifeCycleNotificationHelper = this.CreateLifeCycleNotificationHelper(); this.HttpApiHandler = new HttpApiHandler(this, logger); #endif }
private readonly DurableClientAttribute attribute; // for rehydrating a Client after a webhook internal DurableClient( DurabilityProvider serviceClient, DurableTaskExtension config, HttpApiHandler httpHandler, DurableClientAttribute attribute) { this.config = config ?? throw new ArgumentNullException(nameof(config)); this.client = new TaskHubClient(serviceClient); this.durabilityProvider = serviceClient; this.traceHelper = config.TraceHelper; this.httpApiHandler = httpHandler; this.hubName = attribute.TaskHub ?? config.Options.HubName; this.attribute = attribute; }
public DurableTaskScaleMonitor( string functionId, FunctionName functionName, string hubName, string storageConnectionString, EndToEndTraceHelper traceHelper, DisconnectedPerformanceMonitor performanceMonitor = null) { this.functionId = functionId; this.functionName = functionName; this.hubName = hubName; this.storageConnectionString = storageConnectionString; this.performanceMonitor = performanceMonitor; this.traceHelper = traceHelper; this.scaleMonitorDescriptor = new ScaleMonitorDescriptor($"{this.functionId}-DurableTaskTrigger-{this.hubName}".ToLower()); }
/// <summary> /// Internal initialization call from the WebJobs host. /// </summary> /// <param name="context">Extension context provided by WebJobs.</param> void IExtensionConfigProvider.Initialize(ExtensionConfigContext context) { ConfigureLoaderHooks(); context.ApplyConfig(this, "DurableTask"); // Register the trigger bindings JobHostConfiguration hostConfig = context.Config; ILogger logger = context.Config.LoggerFactory.CreateLogger(LoggerCategoryName); this.traceHelper = new EndToEndTraceHelper(hostConfig, logger, this.LogReplayEvents); this.httpApiHandler = new HttpApiHandler(this, logger); this.lifeCycleNotificationHelper = new LifeCycleNotificationHelper(this, context); // Register the non-trigger bindings, which have a different model. var bindings = new BindingHelper(this, this.traceHelper); // For 202 support if (this.NotificationUrl == null) { #pragma warning disable CS0618 // Type or member is obsolete this.NotificationUrl = context.GetWebhookHandler(); #pragma warning restore CS0618 // Type or member is obsolete } // Note that the order of the rules is important var rule = context.AddBindingRule <OrchestrationClientAttribute>() .AddConverter <string, StartOrchestrationArgs>(bindings.StringToStartOrchestrationArgs) .AddConverter <JObject, StartOrchestrationArgs>(bindings.JObjectToStartOrchestrationArgs); rule.BindToCollector <StartOrchestrationArgs>(bindings.CreateAsyncCollector); rule.BindToInput <DurableOrchestrationClient>(this.GetClient); context.AddBindingRule <OrchestrationTriggerAttribute>() .BindToTrigger(new OrchestrationTriggerAttributeBindingProvider(this, context, this.traceHelper)); context.AddBindingRule <ActivityTriggerAttribute>() .BindToTrigger(new ActivityTriggerAttributeBindingProvider(this, context, this.traceHelper)); AzureStorageOrchestrationServiceSettings settings = this.GetOrchestrationServiceSettings(); this.orchestrationService = new AzureStorageOrchestrationService(settings); this.taskHubWorker = new TaskHubWorker(this.orchestrationService, this, this); this.taskHubWorker.AddOrchestrationDispatcherMiddleware(this.OrchestrationMiddleware); context.Config.AddService <IOrchestrationService>(this.orchestrationService); }
/// <summary> /// Create an EventSourceListener to capture and log Durable EventSource /// data in Linux. /// </summary> /// <param name="logger">A LinuxAppService logger configured for the current linux host.</param> /// <param name="enableVerbose">If true, durableTask.Core verbose logs are enabled. The opposite if false.</param> /// <param name="traceHelper">A tracing client to log exceptions.</param> /// <param name="durabilityProviderEventSourceName">The durability provider's event source name.</param> public EventSourceListener(LinuxAppServiceLogger logger, bool enableVerbose, EndToEndTraceHelper traceHelper, string durabilityProviderEventSourceName) { this.logger = logger; this.disableVerbose = !enableVerbose; // We track the opposite value ro simplify logic later this.traceHelper = traceHelper; this.durabilityProviderEventSourceName = durabilityProviderEventSourceName; // Check to see if any event sources were created before we knew the event source // name for the durability provider and enable that provider. var eventSourcesToEnable = this.pendingEventSources.Where(eventSource => eventSource.Name == this.durabilityProviderEventSourceName); foreach (var eventSource in eventSourcesToEnable) { this.EnableEvents(eventSource, EventLevel.LogAlways, EventKeywords.All); } this.pendingEventSources.Clear(); }
internal DurableClient( DurabilityProvider serviceClient, HttpApiHandler httpHandler, DurableClientAttribute attribute, MessagePayloadDataConverter messageDataConverter, EndToEndTraceHelper traceHelper, DurableTaskOptions durableTaskOptions) { this.messageDataConverter = messageDataConverter; this.client = new TaskHubClient(serviceClient, this.messageDataConverter); this.durabilityProvider = serviceClient; this.traceHelper = traceHelper; this.httpApiHandler = httpHandler; this.durableTaskOptions = durableTaskOptions; this.hubName = attribute.TaskHub ?? this.durableTaskOptions.HubName; this.attribute = attribute; }
internal void Validate(INameResolver environmentVariableResolver, EndToEndTraceHelper traceHelper) { if (string.IsNullOrEmpty(this.HubName)) { throw new InvalidOperationException($"A non-empty {nameof(this.HubName)} configuration is required."); } if (IsInNonProductionSlot() && this.IsDefaultHubName()) { throw new InvalidOperationException($"Task Hub name must be specified in host.json when using slots. Specified name must not equal the default HubName ({this.defaultHubName})." + "See documentation on Task Hubs for information on how to set this: https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-task-hubs"); } string runtimeLanguage = environmentVariableResolver.Resolve("FUNCTIONS_WORKER_RUNTIME"); if (this.ExtendedSessionsEnabled && runtimeLanguage != null && // If we don't know from the environment variable, don't assume customer isn't .NET !string.Equals(runtimeLanguage, "dotnet", StringComparison.OrdinalIgnoreCase)) { traceHelper.ExtensionWarningEvent( hubName: this.HubName, functionName: string.Empty, instanceId: string.Empty, message: "Durable Functions does not work with extendedSessions = true for non-.NET languages. This value is being set to false instead. See https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-perf-and-scale#extended-sessions for more details."); this.ExtendedSessionsEnabled = false; } this.Notifications.Validate(); if (this.MaxConcurrentActivityFunctions <= 0) { throw new InvalidOperationException($"{nameof(this.MaxConcurrentActivityFunctions)} must be a non-negative integer value."); } if (this.MaxConcurrentOrchestratorFunctions <= 0) { throw new InvalidOperationException($"{nameof(this.MaxConcurrentOrchestratorFunctions)} must be a non-negative integer value."); } }
public LocalHttpListener( EndToEndTraceHelper traceHelper, DurableTaskOptions durableTaskOptions, Func <HttpRequestMessage, Task <HttpResponseMessage> > handler) { this.traceHelper = traceHelper ?? throw new ArgumentNullException(nameof(traceHelper)); this.handler = handler ?? throw new ArgumentNullException(nameof(handler)); this.durableTaskOptions = durableTaskOptions ?? throw new ArgumentNullException(nameof(durableTaskOptions)); #if !FUNCTIONS_V1 this.InternalRpcUri = new Uri($"http://127.0.0.1:{this.GetAvailablePort()}/durabletask/"); var listenUri = new Uri(this.InternalRpcUri.GetLeftPart(UriPartial.Authority)); this.localWebHost = new WebHostBuilder() .UseKestrel() .UseUrls(listenUri.OriginalString) .Configure(a => a.Run(this.HandleRequestAsync)) .Build(); #else // Just use default port for internal Uri. No need to check for port availability since // we won't be listening to this endpoint. this.InternalRpcUri = new Uri($"http://127.0.0.1:{DefaultPort}/durabletask/"); this.localWebHost = new NoOpWebHost(); #endif }
public BindingHelper(DurableTaskExtension config, EndToEndTraceHelper traceHelper) { this.config = config; this.traceHelper = traceHelper; }
/// <summary> /// Create an EventSourceListener to capture and log Durable EventSource /// data in Linux. /// </summary> /// <param name="logger">A LinuxAppService logger configured for the current linux host.</param> /// <param name="enableVerbose">If true, durableTask.Core verbose logs are enabled. The opposite if false.</param> /// <param name="traceHelper">A tracing client to log exceptions.</param> public EventSourceListener(LinuxAppServiceLogger logger, bool enableVerbose, EndToEndTraceHelper traceHelper) { this.logger = logger; this.disableVerbose = !enableVerbose; // We track the opposite value ro simplify logic later this.traceHelper = traceHelper; }
public EventGridLifeCycleNotificationHelper( DurableTaskOptions config, INameResolver nameResolver, EndToEndTraceHelper traceHelper) { this.config = config ?? throw new ArgumentNullException(nameof(config)); this.traceHelper = traceHelper ?? throw new ArgumentNullException(nameof(traceHelper)); if (nameResolver == null) { throw new ArgumentNullException(nameof(nameResolver)); } this.eventGridKeyValue = nameResolver.Resolve(config.EventGridKeySettingName); this.eventGridTopicEndpoint = config.EventGridTopicEndpoint; if (nameResolver.TryResolveWholeString(config.EventGridTopicEndpoint, out var endpoint)) { this.eventGridTopicEndpoint = endpoint; } if (!string.IsNullOrEmpty(this.eventGridTopicEndpoint)) { if (!string.IsNullOrEmpty(config.EventGridKeySettingName)) { this.useTrace = true; var retryStatusCode = config.EventGridPublishRetryHttpStatus? .Where(x => Enum.IsDefined(typeof(HttpStatusCode), x)) .Select(x => (HttpStatusCode)x) .ToArray() ?? Array.Empty <HttpStatusCode>(); if (config.EventGridPublishEventTypes == null || config.EventGridPublishEventTypes.Length == 0) { this.eventGridPublishEventTypes = (OrchestrationRuntimeStatus[])Enum.GetValues(typeof(OrchestrationRuntimeStatus)); } else { var startedIndex = Array.FindIndex(config.EventGridPublishEventTypes, x => x == "Started"); if (startedIndex > -1) { config.EventGridPublishEventTypes[startedIndex] = OrchestrationRuntimeStatus.Running.ToString(); } OrchestrationRuntimeStatus ParseAndvalidateEvents(string @event) { var success = Enum.TryParse(@event, out OrchestrationRuntimeStatus @enum); if (success) { switch (@enum) { case OrchestrationRuntimeStatus.Canceled: case OrchestrationRuntimeStatus.ContinuedAsNew: case OrchestrationRuntimeStatus.Pending: success = false; break; default: break; } } if (!success) { throw new ArgumentException("Failed to start lifecycle notification feature. Unsupported event types detected in 'EventGridPublishEventTypes'. You may only specify one or more of the following 'Started', 'Completed', 'Failed', 'Terminated'."); } return(@enum); } this.eventGridPublishEventTypes = config.EventGridPublishEventTypes.Select( x => ParseAndvalidateEvents(x)).ToArray(); } // Currently, we support Event Grid Custom Topic for notify the lifecycle event of an orchestrator. // For more detail about the Event Grid, please refer this document. // Post to custom topic for Azure Event Grid // https://docs.microsoft.com/en-us/azure/event-grid/post-to-custom-topic this.HttpMessageHandler = config.NotificationHandler ?? new HttpRetryMessageHandler( new HttpClientHandler(), config.EventGridPublishRetryCount, config.EventGridPublishRetryInterval, retryStatusCode); if (string.IsNullOrEmpty(this.eventGridKeyValue)) { throw new ArgumentException($"Failed to start lifecycle notification feature. Please check the configuration values for {config.EventGridKeySettingName} on AppSettings."); } } else { throw new ArgumentException($"Failed to start lifecycle notification feature. Please check the configuration values for {config.EventGridTopicEndpoint} and {config.EventGridKeySettingName}."); } } }