Esempio n. 1
0
        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));
        }
Esempio n. 2
0
        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;
        }
Esempio n. 6
0
        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}.");
                }
            }
        }