Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prioritize retrieval of environment variables from IConfiguration instead of directly #1363

Merged
merged 22 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d96807f
Implemented against Dapr.Client.AspNetCore client
WhitWaldo Aug 14, 2024
d309423
SImplified DaprWorkflow DI registration and updated to use IConfigura…
WhitWaldo Aug 14, 2024
d79db7b
Added missing copyright header
WhitWaldo Aug 14, 2024
09544d6
Updated actor registration to prefer the updated IConfiguration-based…
WhitWaldo Aug 14, 2024
fc42cbc
Merge branch 'master' into iconfiguration-preference
WhitWaldo Oct 14, 2024
fa49f23
Merge branch 'master' into iconfiguration-preference
WhitWaldo Oct 14, 2024
851e797
Adopted accepted proposal's guidelines for favoring different environ…
WhitWaldo Oct 15, 2024
ef25775
Merge remote-tracking branch 'origin/iconfiguration-preference' into …
WhitWaldo Oct 15, 2024
616638f
Made some lines a little more concise, added hostname default to Dapr…
WhitWaldo Oct 15, 2024
d386a5c
Fixed and updated unit tests
WhitWaldo Oct 15, 2024
55f375f
Merge branch 'master' into iconfiguration-preference
WhitWaldo Oct 16, 2024
48e4de8
Updated to put endpoint resolution mechanism in DaprDefaults within D…
WhitWaldo Oct 16, 2024
723d955
Updated packages to fix security advisory https://github.com/advisori…
WhitWaldo Oct 17, 2024
18d6c80
Updated Workflow builder to use DaprDefaults with IConfiguration
WhitWaldo Oct 17, 2024
d803bb4
Merge branch 'master' into iconfiguration-preference
WhitWaldo Oct 17, 2024
01e7cbc
Updating global.json
WhitWaldo Oct 17, 2024
c36bded
Tweaked global.json comment
WhitWaldo Oct 17, 2024
ca8566d
Merge remote-tracking branch 'origin/iconfiguration-preference' into …
WhitWaldo Oct 17, 2024
e3e0a85
Adding braces per nit
WhitWaldo Oct 17, 2024
2ce8d35
Consolidated both registration extension methods to remove duplication
WhitWaldo Oct 17, 2024
91849c6
Merge branch 'master' into iconfiguration-preference
WhitWaldo Oct 17, 2024
d558f1f
Merge branch 'master' into iconfiguration-preference
WhitWaldo Oct 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 110 additions & 6 deletions src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -30,12 +34,9 @@ public static class ActorsServiceCollectionExtensions
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" />.</param>
/// <param name="configure">A delegate used to configure actor options and register actor types.</param>
public static void AddActors(this IServiceCollection services, Action<ActorRuntimeOptions> configure)
public static void AddActors(this IServiceCollection? services, Action<ActorRuntimeOptions>? configure)
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
ArgumentNullException.ThrowIfNull(services, nameof(services));

// Routing and health checks are required dependencies.
services.AddRouting();
Expand All @@ -45,6 +46,16 @@ public static void AddActors(this IServiceCollection services, Action<ActorRunti
services.TryAddSingleton<ActorRuntime>(s =>
{
var options = s.GetRequiredService<IOptions<ActorRuntimeOptions>>().Value;

//Replace the HttpEndpoint with an endpoint prioritizing IConfiguration
var configuration = s.GetService<IConfiguration>();
options.HttpEndpoint = options.HttpEndpoint != "http://127.0.0.1:3500"
WhitWaldo marked this conversation as resolved.
Show resolved Hide resolved
? GetHttpEndpoint(configuration)
WhitWaldo marked this conversation as resolved.
Show resolved Hide resolved
: options.HttpEndpoint;
options.DaprApiToken = string.IsNullOrWhiteSpace(options.DaprApiToken)
? GetApiToken(configuration)
: options.DaprApiToken;

var loggerFactory = s.GetRequiredService<ILoggerFactory>();
var activatorFactory = s.GetRequiredService<ActorActivatorFactory>();
var proxyFactory = s.GetRequiredService<IActorProxyFactory>();
Expand All @@ -54,6 +65,16 @@ public static void AddActors(this IServiceCollection services, Action<ActorRunti
services.TryAddSingleton<IActorProxyFactory>(s =>
{
var options = s.GetRequiredService<IOptions<ActorRuntimeOptions>>().Value;

//Replace the HttpEndpoint with an endpoint prioritizing IConfiguration
var configuration = s.GetService<IConfiguration>();
options.HttpEndpoint = options.HttpEndpoint != "http://127.0.0.1:3500"
? GetHttpEndpoint(configuration)
: options.HttpEndpoint;
options.DaprApiToken = string.IsNullOrWhiteSpace(options.DaprApiToken)
? GetApiToken(configuration)
: options.DaprApiToken;

var factory = new ActorProxyFactory()
{
DefaultOptions =
Expand All @@ -72,5 +93,88 @@ public static void AddActors(this IServiceCollection services, Action<ActorRunti
services.Configure<ActorRuntimeOptions>(configure);
}
}

/// <summary>
/// Retrieves the Dapr API token using a failover approach starting with an optional <see cref="IConfiguration"/>
/// instance, then trying to pull from the well-known environment variable name and then opting for an empty string
/// as a default value.
/// </summary>
/// <returns>The Dapr API token.</returns>
private static string GetApiToken(IConfiguration? configuration) => GetResourceValue(configuration, DaprDefaults.DaprApiTokenName);

/// <summary>
/// Builds the Dapr gRPC endpoint using the value from the IConfiguration, if available, then falling back
/// to the value in the environment variable(s) and finally otherwise using the default value (an empty string).
/// </summary>
/// <remarks>
/// Marked as internal for testing purposes.
/// </remarks>
/// <param name="configuration">An injected instance of the <see cref="IConfiguration"/>.</param>
/// <returns>The built gRPC endpoint.</returns>
private static string GetHttpEndpoint(IConfiguration? configuration)
{
WhitWaldo marked this conversation as resolved.
Show resolved Hide resolved
//Prioritize pulling from IConfiguration with a fallback from pulling from the environment variable directly
var httpEndpoint = GetResourceValue(configuration, DaprDefaults.DaprHttpEndpointName);
var httpPort = GetResourceValue(configuration, DaprDefaults.DaprHttpPortName);
int? parsedGrpcPort = string.IsNullOrWhiteSpace(httpPort) ? null : int.Parse(httpPort);

var endpoint = BuildEndpoint(httpEndpoint, parsedGrpcPort);
return string.IsNullOrWhiteSpace(endpoint) ? $"http://localhost:{DaprDefaults.DefaultHttpPort}/" : endpoint;
}

/// <summary>
/// Retrieves the specified value prioritizing pulling it from <see cref="IConfiguration"/>, falling back
/// to an environment variable, and using an empty string as a default.
/// </summary>
/// <param name="configuration">An instance of an <see cref="IConfiguration"/>.</param>
/// <param name="name">The name of the value to retrieve.</param>
/// <returns>The value of the resource.</returns>
private static string GetResourceValue(IConfiguration? configuration, string name)
{
//Attempt to retrieve first from the configuration
var configurationValue = configuration?.GetValue<string?>(name);
if (configurationValue is not null)
return configurationValue;

//Fall back to the environment variable with the same name or default to an empty string
var envVar = Environment.GetEnvironmentVariable(name);
return envVar ?? string.Empty;
}

/// <summary>
/// Builds the endpoint provided an optional endpoint and optional port.
/// </summary>
/// <remarks>
/// Marked as internal for testing purposes.
/// </remarks>
/// <param name="endpoint">The endpoint.</param>
/// <param name="endpointPort">The port</param>
/// <returns>A constructed endpoint value.</returns>
internal static string BuildEndpoint(string? endpoint, int? endpointPort)
{
if (string.IsNullOrWhiteSpace(endpoint) && endpointPort is null)
return string.Empty;

var endpointBuilder = new UriBuilder();
if (!string.IsNullOrWhiteSpace(endpoint))
{
//Extract the scheme, host and port from the endpoint
var uri = new Uri(endpoint);
endpointBuilder.Scheme = uri.Scheme;
endpointBuilder.Host = uri.Host;
endpointBuilder.Port = uri.Port;

//Update the port if provided separately
if (endpointPort is not null)
endpointBuilder.Port = (int)endpointPort;
WhitWaldo marked this conversation as resolved.
Show resolved Hide resolved
}
else if (string.IsNullOrWhiteSpace(endpoint) && endpointPort is not null)
{
endpointBuilder.Host = "localhost";
endpointBuilder.Port = (int)endpointPort;
}

return endpointBuilder.ToString();
}
}
}
Loading
Loading