From 5548c670f480a9eb7c13efb35899ef959636f5dd Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Fri, 18 Oct 2024 03:20:01 -0500 Subject: [PATCH] Prioritize retrieval of environment variables from IConfiguration instead of directly (#1363) * Implemented against Dapr.Client.AspNetCore and Dapr.Client Signed-off-by: Whit Waldo * SImplified DaprWorkflow DI registration and updated to use IConfiguration preference. Needs testing. Signed-off-by: Whit Waldo * Added missing copyright header Signed-off-by: Whit Waldo * Updated actor registration to prefer the updated IConfiguration-based approach for pulling the HTTP endpoint and API token Signed-off-by: Whit Waldo * Adopted accepted proposal's guidelines for favoring different environment variables for determining the sidecar endpoint. Added notes to explain this in the code going forward. Signed-off-by: Whit Waldo * Made some lines a little more concise, added hostname default to DaprDefaults to use when building endpoints. Signed-off-by: Whit Waldo * Fixed and updated unit tests Signed-off-by: Whit Waldo * Updated to put endpoint resolution mechanism in DaprDefaults within Dapr.Common - updating projects and unit tests Signed-off-by: Whit Waldo * Updated packages to fix security advisory https://github.com/advisories/GHSA-447r-wph3-92pm Signed-off-by: Whit Waldo * Updated Workflow builder to use DaprDefaults with IConfiguration Signed-off-by: Whit Waldo * Updating global.json Signed-off-by: Whit Waldo * Tweaked global.json comment Signed-off-by: Whit Waldo * Adding braces per nit Signed-off-by: Whit Waldo * Consolidated both registration extension methods to remove duplication Signed-off-by: Whit Waldo --------- Signed-off-by: Whit Waldo --- Directory.Packages.props | 3 + all.sln | 7 + global.json | 6 +- .../ActorsServiceCollectionExtensions.cs | 28 +- .../Dapr.Actors.AspNetCore.csproj | 1 + src/Dapr.Actors/Client/ActorProxyOptions.cs | 2 +- src/Dapr.Actors/Dapr.Actors.csproj | 1 + .../Runtime/ActorRuntimeOptions.cs | 22 +- .../DaprAuthenticationOptions.cs | 3 +- .../DaprServiceCollectionExtensions.cs | 101 +-- src/Dapr.Client/DaprClient.cs | 11 +- src/Dapr.Client/DaprClientBuilder.cs | 2 +- src/Dapr.Client/InvocationHandler.cs | 2 +- src/Dapr.Common/Dapr.Common.csproj | 2 + src/Dapr.Common/DaprDefaults.cs | 131 ++-- .../DaprWorkflowClientBuilderFactory.cs | 95 +++ .../WorkflowServiceCollectionExtensions.cs | 135 +--- .../Dapr.Actors.Generators.Test.csproj | 1 + .../DaprServiceCollectionExtensionsTest.cs | 25 +- test/Dapr.Common.Test/Dapr.Common.Test.csproj | 23 + test/Dapr.Common.Test/DaprDefaultTest.cs | 582 ++++++++++++++++++ 21 files changed, 918 insertions(+), 265 deletions(-) create mode 100644 src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs create mode 100644 test/Dapr.Common.Test/Dapr.Common.Test.csproj create mode 100644 test/Dapr.Common.Test/DaprDefaultTest.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index d85020770..842c82aa8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,7 @@ + @@ -26,6 +27,7 @@ + @@ -39,6 +41,7 @@ + diff --git a/all.sln b/all.sln index 1a5d78efb..85ed848a4 100644 --- a/all.sln +++ b/all.sln @@ -122,6 +122,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Protos", "src\Dapr.Pro EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common", "src\Dapr.Common\Dapr.Common.csproj", "{B445B19C-A925-4873-8CB7-8317898B6970}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapr.Common.Test", "test\Dapr.Common.Test\Dapr.Common.Test.csproj", "{CDB47863-BEBD-4841-A807-46D868962521}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -302,6 +304,10 @@ Global {B445B19C-A925-4873-8CB7-8317898B6970}.Debug|Any CPU.Build.0 = Debug|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.ActiveCfg = Release|Any CPU {B445B19C-A925-4873-8CB7-8317898B6970}.Release|Any CPU.Build.0 = Release|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDB47863-BEBD-4841-A807-46D868962521}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -357,6 +363,7 @@ Global {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} {DFBABB04-50E9-42F6-B470-310E1B545638} = {27C5D71D-0721-4221-9286-B94AB07B58CF} {B445B19C-A925-4873-8CB7-8317898B6970} = {27C5D71D-0721-4221-9286-B94AB07B58CF} + {CDB47863-BEBD-4841-A807-46D868962521} = {DD020B34-460F-455F-8D17-CF4A949F100B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} diff --git a/global.json b/global.json index 980f4652d..fe53f92ae 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { - "_comment": "This policy allows the 7.0.101 SDK or patches in that family.", + "_comment": "This policy allows the 8.0.100 SDK or patches in that family.", "sdk": { - "version": "7.0.101", - "rollForward": "latestMajor" + "version": "8.0.100", + "rollForward": "minor" } } \ No newline at end of file diff --git a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs index a7f5d04a2..11f05f4c1 100644 --- a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs +++ b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,9 +11,13 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable + using System; +using Dapr; using Dapr.Actors.Client; using Dapr.Actors.Runtime; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -30,12 +34,9 @@ public static class ActorsServiceCollectionExtensions /// /// The . /// A delegate used to configure actor options and register actor types. - public static void AddActors(this IServiceCollection services, Action configure) + public static void AddActors(this IServiceCollection? services, Action? configure) { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } + ArgumentNullException.ThrowIfNull(services, nameof(services)); // Routing and health checks are required dependencies. services.AddRouting(); @@ -45,6 +46,8 @@ public static void AddActors(this IServiceCollection services, Action(s => { var options = s.GetRequiredService>().Value; + ConfigureActorOptions(s, options); + var loggerFactory = s.GetRequiredService(); var activatorFactory = s.GetRequiredService(); var proxyFactory = s.GetRequiredService(); @@ -54,6 +57,8 @@ public static void AddActors(this IServiceCollection services, Action(s => { var options = s.GetRequiredService>().Value; + ConfigureActorOptions(s, options); + var factory = new ActorProxyFactory() { DefaultOptions = @@ -72,5 +77,16 @@ public static void AddActors(this IServiceCollection services, Action(configure); } } + + private static void ConfigureActorOptions(IServiceProvider serviceProvider, ActorRuntimeOptions options) + { + var configuration = serviceProvider.GetService(); + options.DaprApiToken = !string.IsNullOrWhiteSpace(options.DaprApiToken) + ? options.DaprApiToken + : DaprDefaults.GetDefaultDaprApiToken(configuration); + options.HttpEndpoint = !string.IsNullOrWhiteSpace(options.HttpEndpoint) + ? options.HttpEndpoint + : DaprDefaults.GetDefaultHttpEndpoint(); + } } } diff --git a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj index 82c5863db..ef57e76fe 100644 --- a/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj +++ b/src/Dapr.Actors.AspNetCore/Dapr.Actors.AspNetCore.csproj @@ -11,5 +11,6 @@ + diff --git a/src/Dapr.Actors/Client/ActorProxyOptions.cs b/src/Dapr.Actors/Client/ActorProxyOptions.cs index 665a1dced..2afce852c 100644 --- a/src/Dapr.Actors/Client/ActorProxyOptions.cs +++ b/src/Dapr.Actors/Client/ActorProxyOptions.cs @@ -45,7 +45,7 @@ public JsonSerializerOptions JsonSerializerOptions /// /// The Dapr Api Token that is added to the header for all requests. /// - public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(); + public string DaprApiToken { get; set; } = DaprDefaults.GetDefaultDaprApiToken(null); /// /// Gets or sets the HTTP endpoint URI used to communicate with the Dapr sidecar. diff --git a/src/Dapr.Actors/Dapr.Actors.csproj b/src/Dapr.Actors/Dapr.Actors.csproj index 54d3487b8..bcb8d830f 100644 --- a/src/Dapr.Actors/Dapr.Actors.csproj +++ b/src/Dapr.Actors/Dapr.Actors.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs index 62eaceea6..21c302018 100644 --- a/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs +++ b/src/Dapr.Actors/Runtime/ActorRuntimeOptions.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +#nullable enable + using System; using System.Text.Json; @@ -34,7 +36,7 @@ public sealed class ActorRuntimeOptions }; private bool useJsonSerialization = false; private JsonSerializerOptions jsonSerializerOptions = JsonSerializerDefaults.Web; - private string daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); + private string daprApiToken = string.Empty; private int? remindersStoragePartitions = null; /// @@ -180,19 +182,14 @@ public JsonSerializerOptions JsonSerializerOptions set { - if (value is null) - { - throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorRuntimeOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); - } - - this.jsonSerializerOptions = value; + this.jsonSerializerOptions = value ?? throw new ArgumentNullException(nameof(JsonSerializerOptions), $"{nameof(ActorRuntimeOptions)}.{nameof(JsonSerializerOptions)} cannot be null"); } } /// /// The to add to the headers in requests to Dapr runtime /// - public string DaprApiToken + public string? DaprApiToken { get { @@ -201,12 +198,7 @@ public string DaprApiToken set { - if (value is null) - { - throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); - } - - this.daprApiToken = value; + this.daprApiToken = value ?? throw new ArgumentNullException(nameof(DaprApiToken), $"{nameof(ActorRuntimeOptions)}.{nameof(DaprApiToken)} cannot be null"); } } @@ -241,6 +233,6 @@ public int? RemindersStoragePartitions /// corresponding environment variables. /// /// - public string HttpEndpoint { get; set; } = DaprDefaults.GetDefaultHttpEndpoint(); + public string? HttpEndpoint { get; set; } } } diff --git a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs index 4e073d06c..b12d4d14e 100644 --- a/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs +++ b/src/Dapr.AspNetCore/DaprAuthenticationOptions.cs @@ -11,7 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; using Microsoft.AspNetCore.Authentication; namespace Dapr.AspNetCore @@ -29,6 +28,6 @@ public class DaprAuthenticationOptions : AuthenticationSchemeOptions /// Gets or sets the App API token. /// By default, the token will be read from the APP_API_TOKEN environment variable. /// - public string Token { get; set; } = DaprDefaults.GetDefaultAppApiToken(); + public string Token { get; set; } = DaprDefaults.GetDefaultAppApiToken(null); } } diff --git a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs index 388015b80..52e9110be 100644 --- a/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs +++ b/src/Dapr.AspNetCore/DaprServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,55 +11,76 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Microsoft.Extensions.DependencyInjection -{ - using System; - using System.Linq; - using Dapr.Client; - using Extensions; +#nullable enable + +namespace Microsoft.Extensions.DependencyInjection; + +using System; +using Dapr; +using Dapr.Client; +using Extensions; +using Configuration; +/// +/// Provides extension methods for . +/// +public static class DaprServiceCollectionExtensions +{ /// - /// Provides extension methods for . + /// Adds Dapr client services to the provided . This does not include integration + /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. /// - public static class DaprServiceCollectionExtensions + /// The . + /// + public static void AddDaprClient(this IServiceCollection services, Action? configure = null) { - /// - /// Adds Dapr client services to the provided . This does not include integration - /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. - /// - /// The . - /// - public static void AddDaprClient(this IServiceCollection services, Action configure = null) + ArgumentNullException.ThrowIfNull(services, nameof(services)); + + services.TryAddSingleton(serviceProvider => { - ArgumentNullException.ThrowIfNull(services, nameof(services)); + var builder = CreateDaprClientBuilder(serviceProvider); + configure?.Invoke(builder); + return builder.Build(); + }); + } - services.TryAddSingleton(_ => - { - var builder = new DaprClientBuilder(); - configure?.Invoke(builder); + /// + /// Adds Dapr client services to the provided . This does not include integration + /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. + /// + /// The . + /// + public static void AddDaprClient(this IServiceCollection services, + Action configure) + { + ArgumentNullException.ThrowIfNull(services, nameof(services)); - return builder.Build(); - }); - } - - /// - /// Adds Dapr client services to the provided . This does not include integration - /// with ASP.NET Core MVC. Use the AddDapr() extension method on IMvcBuilder to register MVC integration. - /// - /// The . - /// - public static void AddDaprClient(this IServiceCollection services, - Action configure) + services.TryAddSingleton(serviceProvider => { - ArgumentNullException.ThrowIfNull(services, nameof(services)); + var builder = CreateDaprClientBuilder(serviceProvider); + configure?.Invoke(serviceProvider, builder); + return builder.Build(); + }); + } + + private static DaprClientBuilder CreateDaprClientBuilder(IServiceProvider serviceProvider) + { + var builder = new DaprClientBuilder(); + var configuration = serviceProvider.GetService(); + + // Set the HTTP endpoint, if provided, else use the default endpoint + builder.UseHttpEndpoint(DaprDefaults.GetDefaultHttpEndpoint(configuration)); - services.TryAddSingleton(serviceProvider => - { - var builder = new DaprClientBuilder(); - configure?.Invoke(serviceProvider, builder); + // Set the gRPC endpoint, if provided + builder.UseGrpcEndpoint(DaprDefaults.GetDefaultGrpcEndpoint(configuration)); - return builder.Build(); - }); + // Set the API token, if provided + var apiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + if (!string.IsNullOrWhiteSpace(apiToken)) + { + builder.UseDaprApiToken(apiToken); } + + return builder; } } diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 9f107578f..43c640a69 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -139,17 +139,14 @@ public static HttpClient CreateInvokeHttpClient(string appId = null, string dapr public static CallInvoker CreateInvocationInvoker(string appId, string daprEndpoint = null, string daprApiToken = null) { var channel = GrpcChannel.ForAddress(daprEndpoint ?? DaprDefaults.GetDefaultGrpcEndpoint()); - return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken())); + return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null))); } internal static KeyValuePair? GetDaprApiTokenHeader(string apiToken) { - if (string.IsNullOrWhiteSpace(apiToken)) - { - return null; - } - - return new KeyValuePair("dapr-api-token", apiToken); + return string.IsNullOrWhiteSpace(apiToken) + ? null + : new KeyValuePair("dapr-api-token", apiToken); } /// diff --git a/src/Dapr.Client/DaprClientBuilder.cs b/src/Dapr.Client/DaprClientBuilder.cs index 50a4979d1..68315c45b 100644 --- a/src/Dapr.Client/DaprClientBuilder.cs +++ b/src/Dapr.Client/DaprClientBuilder.cs @@ -40,7 +40,7 @@ public DaprClientBuilder() }; this.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); - this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(); + this.DaprApiToken = DaprDefaults.GetDefaultDaprApiToken(null); } // property exposed for testing purposes diff --git a/src/Dapr.Client/InvocationHandler.cs b/src/Dapr.Client/InvocationHandler.cs index 1b55436aa..36fd6b77f 100644 --- a/src/Dapr.Client/InvocationHandler.cs +++ b/src/Dapr.Client/InvocationHandler.cs @@ -51,7 +51,7 @@ public class InvocationHandler : DelegatingHandler public InvocationHandler() { this.parsedEndpoint = new Uri(DaprDefaults.GetDefaultHttpEndpoint(), UriKind.Absolute); - this.apiToken = DaprDefaults.GetDefaultDaprApiToken(); + this.apiToken = DaprDefaults.GetDefaultDaprApiToken(null); } /// diff --git a/src/Dapr.Common/Dapr.Common.csproj b/src/Dapr.Common/Dapr.Common.csproj index ea3e8ae84..31af3952c 100644 --- a/src/Dapr.Common/Dapr.Common.csproj +++ b/src/Dapr.Common/Dapr.Common.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/Dapr.Common/DaprDefaults.cs b/src/Dapr.Common/DaprDefaults.cs index 575a3c148..85a4b18c8 100644 --- a/src/Dapr.Common/DaprDefaults.cs +++ b/src/Dapr.Common/DaprDefaults.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using Microsoft.Extensions.Configuration; + namespace Dapr { internal static class DaprDefaults @@ -20,82 +22,111 @@ internal static class DaprDefaults private static string daprApiToken = string.Empty; private static string appApiToken = string.Empty; + public const string DaprApiTokenName = "DAPR_API_TOKEN"; + public const string AppApiTokenName = "APP_API_TOKEN"; + public const string DaprHttpEndpointName = "DAPR_HTTP_ENDPOINT"; + public const string DaprHttpPortName = "DAPR_HTTP_PORT"; + public const string DaprGrpcEndpointName = "DAPR_GRPC_ENDPOINT"; + public const string DaprGrpcPortName = "DAPR_GRPC_PORT"; + + public const string DefaultDaprScheme = "http"; + public const string DefaultDaprHost = "localhost"; + public const int DefaultHttpPort = 3500; + public const int DefaultGrpcPort = 50001; + /// /// Get the value of environment variable DAPR_API_TOKEN /// + /// The optional to pull the value from. /// The value of environment variable DAPR_API_TOKEN - public static string GetDefaultDaprApiToken() - { - // Lazy-init is safe because this is just populating the default - // We don't plan to support the case where the user changes environment variables - // for a running process. - if (string.IsNullOrEmpty(daprApiToken)) - { - // Treat empty the same as null since it's an environment variable - var value = Environment.GetEnvironmentVariable("DAPR_API_TOKEN"); - daprApiToken = string.IsNullOrEmpty(value) ? string.Empty : value; - } - - return daprApiToken; - } + public static string GetDefaultDaprApiToken(IConfiguration? configuration) => + GetResourceValue(configuration, DaprApiTokenName) ?? string.Empty; /// /// Get the value of environment variable APP_API_TOKEN /// + /// The optional to pull the value from. /// The value of environment variable APP_API_TOKEN - public static string GetDefaultAppApiToken() - { - if (string.IsNullOrEmpty(appApiToken)) - { - var value = Environment.GetEnvironmentVariable("APP_API_TOKEN"); - appApiToken = string.IsNullOrEmpty(value) ? string.Empty : value; - } - - return appApiToken; - } + public static string GetDefaultAppApiToken(IConfiguration? configuration) => + GetResourceValue(configuration, AppApiTokenName) ?? string.Empty; /// /// Get the value of HTTP endpoint based off environment variables /// + /// The optional to pull the value from. /// The value of HTTP endpoint based off environment variables - public static string GetDefaultHttpEndpoint() + public static string GetDefaultHttpEndpoint(IConfiguration? configuration = null) { - if (string.IsNullOrEmpty(httpEndpoint)) - { - var endpoint = Environment.GetEnvironmentVariable("DAPR_HTTP_ENDPOINT"); - if (!string.IsNullOrEmpty(endpoint)) { - httpEndpoint = endpoint; - return httpEndpoint; - } - - var port = Environment.GetEnvironmentVariable("DAPR_HTTP_PORT"); - port = string.IsNullOrEmpty(port) ? "3500" : port; - httpEndpoint = $"http://127.0.0.1:{port}"; - } + //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated + var endpoint = GetResourceValue(configuration, DaprHttpEndpointName); + var port = GetResourceValue(configuration, DaprHttpPortName); + + //Use the default HTTP port if we're unable to retrieve/parse the provided port + int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultHttpPort : int.Parse(port); - return httpEndpoint; + return BuildEndpoint(endpoint, parsedGrpcPort.Value); } /// /// Get the value of gRPC endpoint based off environment variables /// + /// The optional to pull the value from. /// The value of gRPC endpoint based off environment variables - public static string GetDefaultGrpcEndpoint() + public static string GetDefaultGrpcEndpoint(IConfiguration? configuration = null) { - if (string.IsNullOrEmpty(grpcEndpoint)) - { - var endpoint = Environment.GetEnvironmentVariable("DAPR_GRPC_ENDPOINT"); - if (!string.IsNullOrEmpty(endpoint)) { - grpcEndpoint = endpoint; - return grpcEndpoint; - } + //Prioritize pulling from the IConfiguration and fallback to the environment variable if not populated + var endpoint = GetResourceValue(configuration, DaprGrpcEndpointName); + var port = GetResourceValue(configuration, DaprGrpcPortName); + + //Use the default gRPC port if we're unable to retrieve/parse the provided port + int? parsedGrpcPort = string.IsNullOrWhiteSpace(port) ? DefaultGrpcPort : int.Parse(port); + + return BuildEndpoint(endpoint, parsedGrpcPort.Value); + } + + /// + /// Builds the Dapr endpoint. + /// + /// The endpoint value. + /// The endpoint port value, whether pulled from configuration/envvar or the default. + /// A constructed endpoint value. + private static string BuildEndpoint(string? endpoint, int endpointPort) + { + var endpointBuilder = new UriBuilder { Scheme = DefaultDaprScheme, Host = DefaultDaprHost }; //Port depends on endpoint - var port = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - port = string.IsNullOrEmpty(port) ? "50001" : port; - grpcEndpoint = $"http://127.0.0.1:{port}"; + if (!string.IsNullOrWhiteSpace(endpoint)) //If the endpoint is set, it doesn't matter if the port is + { + //Extract the scheme, host and port from the endpoint and replace defaults + var uri = new Uri(endpoint); + endpointBuilder.Scheme = uri.Scheme; + endpointBuilder.Host = uri.Host; + endpointBuilder.Port = uri.Port; } + else + { + //Should only set the port if the endpoint isn't populated + endpointBuilder.Port = endpointPort; + } + + return endpointBuilder.ToString(); + } + + /// + /// Retrieves the specified value prioritizing pulling it from , falling back + /// to an environment variable, and using an empty string as a default. + /// + /// An instance of an . + /// The name of the value to retrieve. + /// The value of the resource. + private static string? GetResourceValue(IConfiguration? configuration, string name) + { + //Attempt to retrieve first from the configuration + var configurationValue = configuration?[name]; + if (configurationValue is not null) + return configurationValue; - return grpcEndpoint; + //Fall back to the environment variable with the same name or default to an empty string + return Environment.GetEnvironmentVariable(name); } } } diff --git a/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs new file mode 100644 index 000000000..7a854cf05 --- /dev/null +++ b/src/Dapr.Workflow/DaprWorkflowClientBuilderFactory.cs @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------ +// Copyright 2024 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------ + +using System; +using System.Net.Http; +using Grpc.Net.Client; +using Microsoft.DurableTask.Client; +using Microsoft.DurableTask.Worker; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +#nullable enable + +namespace Dapr.Workflow; + +/// +/// A factory for building a . +/// +internal sealed class DaprWorkflowClientBuilderFactory +{ + private readonly IConfiguration _configuration; + private readonly IHttpClientFactory _httpClientFactory; + private readonly IServiceCollection _services; + + /// + /// Constructor used to inject the required types into the factory. + /// + public DaprWorkflowClientBuilderFactory(IConfiguration configuration, IHttpClientFactory httpClientFactory, IServiceCollection services) + { + _configuration = configuration; + _httpClientFactory = httpClientFactory; + _services = services; + } + + /// + /// Responsible for building the client itself. + /// + /// + public void CreateClientBuilder(Action configure) + { + _services.AddDurableTaskClient(builder => + { + var apiToken = DaprDefaults.GetDefaultDaprApiToken(_configuration); + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(_configuration); + + var httpClient = _httpClientFactory.CreateClient(); + + if (!string.IsNullOrWhiteSpace(apiToken)) + { + httpClient.DefaultRequestHeaders.Add( "Dapr-Api-Token", apiToken); + } + + builder.UseGrpc(GrpcChannel.ForAddress(grpcEndpoint, new GrpcChannelOptions { HttpClient = httpClient })); + builder.RegisterDirectly(); + }); + + _services.AddDurableTaskWorker(builder => + { + WorkflowRuntimeOptions options = new(); + configure?.Invoke(options); + + var apiToken = DaprDefaults.GetDefaultDaprApiToken(_configuration); + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(_configuration); + + if (!string.IsNullOrEmpty(grpcEndpoint)) + { + var httpClient = _httpClientFactory.CreateClient(); + + if (!string.IsNullOrWhiteSpace(apiToken)) + { + httpClient.DefaultRequestHeaders.Add("Dapr-Api-Token", apiToken); + } + + builder.UseGrpc( + GrpcChannel.ForAddress(grpcEndpoint, new GrpcChannelOptions { HttpClient = httpClient })); + } + else + { + builder.UseGrpc(); + } + + builder.AddTasks(registry => options.AddWorkflowsAndActivitiesToRegistry(registry)); + }); + } +} diff --git a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs index ca514f221..3c19583aa 100644 --- a/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs +++ b/src/Dapr.Workflow/WorkflowServiceCollectionExtensions.cs @@ -14,20 +14,14 @@ namespace Dapr.Workflow { using System; - using Grpc.Net.Client; - using Microsoft.DurableTask.Client; - using Microsoft.DurableTask.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; - using System.Net.Http; - using Dapr; /// /// Contains extension methods for using Dapr Workflow with dependency injection. /// public static class WorkflowServiceCollectionExtensions { - /// /// Adds Dapr Workflow support to the service collection. /// @@ -43,6 +37,7 @@ public static IServiceCollection AddDaprWorkflow( } serviceCollection.TryAddSingleton(); + serviceCollection.AddHttpClient(); #pragma warning disable CS0618 // Type or member is obsolete - keeping around temporarily - replaced by DaprWorkflowClient serviceCollection.TryAddSingleton(); @@ -50,135 +45,17 @@ public static IServiceCollection AddDaprWorkflow( serviceCollection.AddHostedService(); serviceCollection.TryAddSingleton(); serviceCollection.AddDaprClient(); - serviceCollection.AddDaprWorkflowClient(); - + serviceCollection.AddOptions().Configure(configure); - serviceCollection.AddDurableTaskWorker(builder => + serviceCollection.AddSingleton(c => { - WorkflowRuntimeOptions options = new(); - configure?.Invoke(options); - - if (TryGetGrpcAddress(out string address)) - { - var daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); - if (!string.IsNullOrEmpty(daprApiToken)) - { - var client = new HttpClient(); - client.DefaultRequestHeaders.Add("Dapr-Api-Token", daprApiToken); - builder.UseGrpc(CreateChannel(address, client)); - } - else - { - builder.UseGrpc(address); - } - - } - else - { - builder.UseGrpc(); - } - - builder.AddTasks(registry => options.AddWorkflowsAndActivitiesToRegistry(registry)); + var factory = c.GetRequiredService(); + factory.CreateClientBuilder(configure); + return new object(); //Placeholder as actual registration is performed inside factory }); return serviceCollection; } - - /// - /// Adds Dapr Workflow client support to the service collection. - /// - /// - /// Use this extension method if you want to use in your app - /// but don't wish to define any workflows or activities. - /// - /// The . - public static IServiceCollection AddDaprWorkflowClient(this IServiceCollection services) - { - services.TryAddSingleton(); - services.AddDurableTaskClient(builder => - { - if (TryGetGrpcAddress(out string address)) - { - var daprApiToken = DaprDefaults.GetDefaultDaprApiToken(); - if (!string.IsNullOrEmpty(daprApiToken)) - { - var client = new HttpClient(); - client.DefaultRequestHeaders.Add("Dapr-Api-Token", daprApiToken); - builder.UseGrpc(CreateChannel(address, client)); - } - else - { - builder.UseGrpc(address); - } - - } - else - { - builder.UseGrpc(); - } - - builder.RegisterDirectly(); - }); - - return services; - } - - static bool TryGetGrpcAddress(out string address) - { - // TODO: Ideally we should be using DaprDefaults.cs for this. However, there are two blockers: - // 1. DaprDefaults.cs uses 127.0.0.1 instead of localhost, which prevents testing with Dapr on WSL2 and the app on Windows - // 2. DaprDefaults.cs doesn't compile when the project has C# nullable reference types enabled. - // If the above issues are fixed (ensuring we don't regress anything) we should switch to using the logic in DaprDefaults.cs. - var daprEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); - if (!String.IsNullOrEmpty(daprEndpoint)) { - address = daprEndpoint; - return true; - } - - var daprPortStr = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - if (int.TryParse(daprPortStr, out int daprGrpcPort)) - { - // There is a bug in the Durable Task SDK that requires us to change the format of the address - // depending on the version of .NET that we're targeting. For now, we work around this manually. -#if NET6_0_OR_GREATER - address = $"http://localhost:{daprGrpcPort}"; -#else - address = $"localhost:{daprGrpcPort}"; -#endif - return true; - } - - address = string.Empty; - return false; - } - - static GrpcChannel CreateChannel(string address, HttpClient client) - { - - GrpcChannelOptions options = new() { HttpClient = client}; - var daprEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(); - if (!String.IsNullOrEmpty(daprEndpoint)) { - return GrpcChannel.ForAddress(daprEndpoint, options); - } - - var daprPortStr = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); - if (int.TryParse(daprPortStr, out int daprGrpcPort)) - { - // If there is no address passed in, we default to localhost - if (String.IsNullOrEmpty(address)) - { - // There is a bug in the Durable Task SDK that requires us to change the format of the address - // depending on the version of .NET that we're targeting. For now, we work around this manually. - #if NET6_0_OR_GREATER - address = $"http://localhost:{daprGrpcPort}"; - #else - address = $"localhost:{daprGrpcPort}"; - #endif - } - - } - return GrpcChannel.ForAddress(address, options); - } } } diff --git a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj index 91c7e8b42..80a79cafe 100644 --- a/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj +++ b/test/Dapr.Actors.Generators.Test/Dapr.Actors.Generators.Test.csproj @@ -27,6 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs index a82948cf3..4a340e22a 100644 --- a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs +++ b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,9 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System; +#nullable enable + +using System; using System.Text.Json; using Dapr.Client; using Microsoft.Extensions.DependencyInjection; @@ -43,15 +45,16 @@ public void AddDaprClient_RegistersDaprClientOnlyOnce() var serviceProvider = services.BuildServiceProvider(); - DaprClientGrpc daprClient = serviceProvider.GetService() as DaprClientGrpc; + DaprClientGrpc? daprClient = serviceProvider.GetService() as DaprClientGrpc; - Assert.True(daprClient.JsonSerializerOptions.PropertyNameCaseInsensitive); + Assert.NotNull(daprClient); + Assert.True(daprClient?.JsonSerializerOptions.PropertyNameCaseInsensitive); } [Fact] public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider() { - + var services = new ServiceCollection(); services.AddSingleton(); services.AddDaprClient((provider, builder) => @@ -66,13 +69,15 @@ public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider() }); var serviceProvider = services.BuildServiceProvider(); - - DaprClientGrpc client = serviceProvider.GetRequiredService() as DaprClientGrpc; - + + DaprClientGrpc? client = serviceProvider.GetRequiredService() as DaprClientGrpc; + //Registers with case-insensitive as true by default, but we set as false above - Assert.False(client.JsonSerializerOptions.PropertyNameCaseInsensitive); + Assert.NotNull(client); + Assert.False(client?.JsonSerializerOptions.PropertyNameCaseInsensitive); } + #if NET8_0_OR_GREATER [Fact] public void AddDaprClient_WithKeyedServices() @@ -90,7 +95,7 @@ public void AddDaprClient_WithKeyedServices() Assert.NotNull(daprClient); } #endif - + private class TestConfigurationProvider { public bool GetCaseSensitivity() => false; diff --git a/test/Dapr.Common.Test/Dapr.Common.Test.csproj b/test/Dapr.Common.Test/Dapr.Common.Test.csproj new file mode 100644 index 000000000..6e34c3a7a --- /dev/null +++ b/test/Dapr.Common.Test/Dapr.Common.Test.csproj @@ -0,0 +1,23 @@ + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/test/Dapr.Common.Test/DaprDefaultTest.cs b/test/Dapr.Common.Test/DaprDefaultTest.cs new file mode 100644 index 000000000..ef4d0da3c --- /dev/null +++ b/test/Dapr.Common.Test/DaprDefaultTest.cs @@ -0,0 +1,582 @@ +using System; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Dapr.Common.Test; + +public class DaprDefaultTest +{ + [Fact] + public void ShouldBuildHttpEndpointAndPortUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "test_DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointAndPortUsingConfiguration() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "test_DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingConfiguration() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingOnlyPortConfiguration() + { + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:2569/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingEnvVarValues() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "http://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://dapr.io:80/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildHttpEndpointUsingMixedValues() + { + const string endpointVarName = "test_DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2569"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + configurationBuilder.AddEnvironmentVariables("test_"); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("https://dapr.io:443/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToEmptyHttpEndpoint() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:3500/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToLocalhostWithPort() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, "7256"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:7256/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldDefaultToLocalhostWithDefaultPort() + { + const string endpointVarName = "DAPR_HTTP_ENDPOINT"; + const string portVarName = "DAPR_HTTP_PORT"; + var original_HttpEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_HttpPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(configuration); + Assert.Equal("http://localhost:3500/", httpEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_HttpEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_HttpPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_GRPC_ENDPOINT"; + const string portVarName = "test_DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "2570"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingConfiguration() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable(portVarName, null, EnvironmentVariableTarget.Process); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingPrefixedConfiguration() + { + const string endpointVarName = "test_DAPR_GRPC_ENDPOINT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + + try + { + const string prefix = "test_"; + + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingConfiguration() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointAndPortUsingEnvVarValues() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, "4744"); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointUsingEnvVarValues() + { + const string endpointVarName = "DAPR_GRPC_ENDPOINT"; + const string portVarName = "DAPR_GRPC_PORT"; + var original_GrpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_GrpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, "https://grpc.dapr.io"); + Environment.SetEnvironmentVariable(portVarName, null); //Will use 443 from the endpoint value instead + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal("https://grpc.dapr.io:443/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_GrpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_GrpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointDefaultToLocalhostWithPort() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_grpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_grpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, "7256"); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal($"{DaprDefaults.DefaultDaprScheme}://{DaprDefaults.DefaultDaprHost}:7256/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_grpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_grpcPort); + } + } + + [Fact] + public void ShouldBuildGrpcEndpointDefaultToLocalhostWithDefaultPort() + { + const string endpointVarName = DaprDefaults.DaprGrpcEndpointName; + const string portVarName = DaprDefaults.DaprGrpcPortName; + var original_grpcEndpoint = Environment.GetEnvironmentVariable(endpointVarName); + var original_grpcPort = Environment.GetEnvironmentVariable(portVarName); + + try + { + Environment.SetEnvironmentVariable(endpointVarName, null); + Environment.SetEnvironmentVariable(portVarName, null); + + var configurationBuilder = new ConfigurationBuilder(); + var configuration = configurationBuilder.Build(); + + var grpcEndpoint = DaprDefaults.GetDefaultGrpcEndpoint(configuration); + Assert.Equal($"{DaprDefaults.DefaultDaprScheme}://{DaprDefaults.DefaultDaprHost}:{DaprDefaults.DefaultGrpcPort}/", grpcEndpoint); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(endpointVarName, original_grpcEndpoint); + Environment.SetEnvironmentVariable(portVarName, original_grpcPort); + } + } + + [Fact] + public void ShouldBuildApiTokenUsingConfiguration() + { + const string envVarName = DaprDefaults.DaprApiTokenName; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + + try + { + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } + + [Fact] + public void ShouldBuildApiTokenUsingPrefixedConfiguration() + { + + const string envVarName = $"test_{DaprDefaults.DaprApiTokenName}"; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + + try + { + const string prefix = "test_"; + + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(prefix); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } + + [Fact] + public void ShouldBuildApiTokenWithEnvVarWhenConfigurationNotAvailable() + { + const string envVarName = DaprDefaults.DaprApiTokenName; + var original_ApiToken = Environment.GetEnvironmentVariable(envVarName); + const string apiToken = "abc123"; + Environment.SetEnvironmentVariable(envVarName, apiToken); + + try + { + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddEnvironmentVariables(); + var configuration = configurationBuilder.Build(); + + var testApiToken = DaprDefaults.GetDefaultDaprApiToken(configuration); + Assert.Equal(apiToken, testApiToken); + } + finally + { + //Restore + Environment.SetEnvironmentVariable(envVarName, original_ApiToken); + } + } +}