-
Notifications
You must be signed in to change notification settings - Fork 34
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
durable task scheduler auth extension support #362
base: main
Are you sure you want to change the base?
Conversation
save initial
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great! I added some comments.
@jviau would you mind taking a look at this as well? The main feedback I'm interested in from you is the package name/organization. The idea behind this PR is to introduce a mechanism for configuring the gRPC channels to work with the Durable Task Scheduler (DTS). This includes setting up the appropriate auth and some other service-specific headers (like the task hub name). I figured this might be the right time to introduce an "Extensions" concept and make "Azure" the extension consumers should use for talking to DTS. There could be others in the future. WDYT? |
This reverts commit 131c575.
|
||
static GrpcChannel GetGrpcChannelForOptions(DurableTaskSchedulerOptions options) | ||
{ | ||
if (string.IsNullOrEmpty(options.EndpointAddress)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a set of helper methods for this, Check.Argument(...)
|
||
if (!endpoint.Contains("://")) | ||
{ | ||
endpoint = $"https://{endpoint}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is performing this kind of manipulation standard practice? Asking because I wonder if users would be confused by https://
being appended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what the best practice is, but we did this because a ton of people were making mistakes in terms of whether or not to include the https://
prefix, so we decided to just support both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. I wonder if we can move the manipulation to the options object, so it will prepend the https://
in the setter of Endpoint
.
return new ArgumentException(message: $"Required option '{optionName}' was not provided."); | ||
} | ||
|
||
sealed class TokenCache(TokenCredential credential, TokenRequestContext context, TimeSpan margin) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you look into Azure.Identity
library to see if they already do token caching?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this: maybe you can copy and adapt it for usage here: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/src/Pipeline/BearerTokenAuthenticationPolicy.cs#L219
builder.UseGrpc(GetGrpcChannelForOptions(options)); | ||
} | ||
|
||
static GrpcChannel GetGrpcChannelForOptions(DurableTaskSchedulerOptions options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small suggestion: I like putting options-projecting methods like this on the options object itself (as internal
).
|
||
string resourceId = options.ResourceId ?? "https://durabletask.io"; | ||
int processId = Environment.ProcessId; | ||
string workerId = options.WorkerId ?? $"{Environment.MachineName},{processId},{Guid.NewGuid():N}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any formality to worker ID creation? Also, I recommend putting this default-logic into the options object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No formality yet. We expect to iterate on this over time.
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure", "src\Extensions\Azure\Azure.csproj", "{662BF73D-A4DD-4910-8625-7C12F1ACDBEC}" | ||
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extensions.Azure.Tests", "test\Extensions.Azure.Tests\Extensions.Azure.Tests.csproj", "{DBB5DB4E-A1B0-4C86-A233-213789C46929}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mirror src
layout in test
please. So, this project should be in test/Extensions/Azure.Tests.csproj
|
||
<ItemGroup> | ||
<PackageReference Include="Azure.Identity" Version="1.13.1" /> | ||
<PackageReference Include="Grpc.Net.Client" Version="2.67.0" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is also a package Grpc.Net.ClientFactory
which adds constructing grpc clients via DI. Have you evaluated if that is applicable here? I think one of the major benefits to it is how the underlying HttpClient is managed. Not saying it is mandatory, but want to make sure it is evaluated and either adopted or ruled out.
/// Gets or sets the resource ID of the Durable Task Scheduler resource. | ||
/// The default value is https://durabletask.io. | ||
/// </summary> | ||
public string? ResourceId { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed, or can it be parsed from EndpointAddress
?
How will this work in other clouds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not part of the endpoint address. We could include it as part of the connection string, but I'm not sure yet whether/when we'll need to make this generally configurable. I suggest we not worry about this until we're actually ready to test other clouds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh it isn't part of the address? So endpoints aren't of the form {my-resource}.{region}.durabletask.io
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No - this is an OAuth 2.0 resource ID. It represents the entire DTS service owned by us. It's not a customer-owned scheduler resource.
{ | ||
DurableTaskSchedulerOptions options = new(endpointAddress, taskHubName, credential); | ||
|
||
configure?.Invoke(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class should leverage options configuration instead of directly calling configure?.Invoke
.
IE:
builder.Services.Configure(builder.Name, configure);
You will then need to make some adjustments to UseGrpc
call:
// method body
if (configure is not null)
{
builder.Services.Configure(builder.Name, configure); // from above, to configure this options object
}
builder.Services.TryAddEnumerable(
ServiceDescription.Singleton<IConfigureOptions<GrpcDurableTaskWorkerOptions>, ConfigureGrpcChannel>());
builder.UseGrpc(_ => { }); // empty callback as the line above does the work. This call is to get the build target registered.
// end method body
// helper internal class
class ConfigureGrpcChannel(IOptionsMonitor<DurableTaskSchedulerOptions> schedulerOptions) : IConfigureNamedOptions<GrpcDurableTaskWorkerOptions>
{
public void Configure(GrpcDurableTaskWorkerOptions options) => this.Configure(Options.DefaultName, options);
public void Configure(string name, GrpcDurableTaskWorkerOptions options)
{
DurableTaskSchedulerOptions source = schedulerOptions.Get(name);
options.Channel = source.CreateChannel(); // method added to options object.
}
}
The benefit here is now users can call chain and they can use options configuration directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want to also mention that this is an important change. It will allow for more flexible configuration of this, such as consuming configuration from more varied sources (such as Azure AppConfiguration). With this, an advanced user could write the following code:
builder.Services.AddOptions<DurableTaskSchedulerOptions>(builder.Name)
.Configure<IConfiguration>((options, config) => options.Endpoint = config["SomeConfigKeyThatHasMyEndpoint"]);
/// <summary> | ||
/// Initializes a new instance of the <see cref="DurableTaskSchedulerOptions"/> class. | ||
/// </summary> | ||
internal DurableTaskSchedulerOptions(string endpointAddress, string taskHubName, TokenCredential? credential = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To follow options pattern, this will need a parameterless constructor. You can leverage options validation to ensure all required fields are set.
This pull request introduces a new set of Azure extensions for the Durable Task Framework, including project setup, implementation of core classes and methods, and unit tests. The most important changes include adding new projects to the solution, defining the core classes and methods for Azure integration, and creating unit tests to ensure the functionality of the new extensions.
Project Setup:
Extensions
andAzure
to the solutionMicrosoft.DurableTask.sln
and configured their build settings. [1] [2] [3]Core Implementation:
Azure.csproj
file to define the Azure extensions project, including dependencies and target frameworks.DurableTaskSchedulerConnectionString
class to parse and manage connection strings for the Durable Task Scheduler service.DurableTaskSchedulerExtensions
class with methods to configure Durable Task Worker and Client to use the Durable Task Scheduler service.DurableTaskSchedulerOptions
class to encapsulate configuration options for the Durable Task Scheduler.Unit Tests:
DurableTaskSchedulerConnectionStringTests.cs
to validate the parsing and handling of connection strings.