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)); }
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)); }
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(); }
public AzureStorageDurabilityProviderFactory( IOptions <DurableTaskOptions> options, IConnectionStringResolver connectionStringResolver) { this.options = options.Value; this.azureStorageOptions = new AzureStorageOptions(); JsonConvert.PopulateObject(JsonConvert.SerializeObject(this.options.StorageProvider), this.azureStorageOptions); this.azureStorageOptions.Validate(); this.connectionStringResolver = connectionStringResolver ?? throw new ArgumentNullException(nameof(connectionStringResolver)); this.defaultConnectionName = this.azureStorageOptions.ConnectionStringName ?? ConnectionStringNames.Storage; }
public AzureStorageDurabilityProviderFactory( IOptions <DurableTaskOptions> options, IConnectionStringResolver connectionStringResolver, INameResolver nameResolver, ILoggerFactory loggerFactory, #pragma warning disable CS0612 // Type or member is obsolete IPlatformInformationService platformInfo) #pragma warning restore CS0612 // Type or member is obsolete { this.options = options.Value; this.nameResolver = nameResolver; this.loggerFactory = loggerFactory; this.azureStorageOptions = new AzureStorageOptions(); this.inConsumption = platformInfo.InConsumption(); // The consumption plan has different performance characteristics so we provide // different defaults for key configuration values. int maxConcurrentOrchestratorsDefault = this.inConsumption ? 5 : 10 * Environment.ProcessorCount; int maxConcurrentActivitiesDefault = this.inConsumption ? 10 : 10 * Environment.ProcessorCount; if (this.inConsumption) { if (platformInfo.IsPython()) { this.azureStorageOptions.ControlQueueBufferThreshold = 32; } else { this.azureStorageOptions.ControlQueueBufferThreshold = 128; } } // The following defaults are only applied if the customer did not explicitely set them on `host.json` this.options.MaxConcurrentOrchestratorFunctions = this.options.MaxConcurrentOrchestratorFunctions ?? maxConcurrentOrchestratorsDefault; this.options.MaxConcurrentActivityFunctions = this.options.MaxConcurrentActivityFunctions ?? maxConcurrentActivitiesDefault; // Override the configuration defaults with user-provided values in host.json, if any. JsonConvert.PopulateObject(JsonConvert.SerializeObject(this.options.StorageProvider), this.azureStorageOptions); var logger = loggerFactory.CreateLogger(nameof(this.azureStorageOptions)); this.azureStorageOptions.Validate(logger); this.connectionStringResolver = connectionStringResolver ?? throw new ArgumentNullException(nameof(connectionStringResolver)); this.defaultConnectionName = this.azureStorageOptions.ConnectionStringName ?? ConnectionStringNames.Storage; }
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; }
public AzureStorageDurabilityProviderFactory( IOptions <DurableTaskOptions> options, IConnectionStringResolver connectionStringResolver) { this.options = options.Value; this.azureStorageOptions = new AzureStorageOptions(); JsonConvert.PopulateObject(JsonConvert.SerializeObject(this.options.StorageProvider), this.azureStorageOptions); this.azureStorageOptions.Validate(); if (!this.options.IsDefaultHubName()) { this.azureStorageOptions.ValidateHubName(this.options.HubName); } else if (!this.azureStorageOptions.IsSanitizedHubName(this.options.HubName, out string sanitizedHubName)) { this.options.SetDefaultHubName(sanitizedHubName); } this.connectionStringResolver = connectionStringResolver; this.defaultConnectionName = this.azureStorageOptions.ConnectionStringName ?? ConnectionStringNames.Storage; this.defaultSettings = this.GetAzureStorageOrchestrationServiceSettings(); }
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 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}."); } } }