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

durable task scheduler auth extension support #362

Open
wants to merge 51 commits into
base: main
Choose a base branch
from

Conversation

YunchuWang
Copy link
Member

@YunchuWang YunchuWang commented Jan 9, 2025

This pull request introduces several significant changes, primarily focusing on the setup of new projects and the integration of Azure Managed extensions for the Durable Task Framework client. The most important changes include the addition of new projects to the solution, updates to the build configuration, and the implementation of new client extensions and options for Azure Managed services.

New Projects and Build Configuration:

  • Added new projects Worker.AzureManaged, Client.AzureManaged, Shared.AzureManaged.Tests, Client.AzureManaged.Tests, and Worker.AzureManaged.Tests to the solution file Microsoft.DurableTask.sln.
  • Updated the build configuration in Microsoft.DurableTask.sln to include the newly added projects. [1] [2]
  • Set up .NET 6.0 in the GitHub Actions workflow file .github/workflows/validate-build.yml.

Azure Managed Client Implementation:

  • Created the Client.AzureManaged.csproj project file with necessary package references and project dependencies.
  • Implemented DurableTaskSchedulerClientExtensions to provide extension methods for configuring Durable Task clients to use the Azure Durable Task Scheduler service.
  • Defined DurableTaskSchedulerClientOptions to configure the Durable Task Scheduler, including methods for creating gRPC channels and handling connection strings.
  • Added AccessTokenCache to manage and refresh Azure access tokens.
  • Implemented DurableTaskSchedulerConnectionString to parse and handle connection strings for the Durable Task Scheduler service.

Copy link
Member

@cgillum cgillum left a 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.

src/Extensions/Azure/DurableTaskSchedulerOptions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
@cgillum
Copy link
Member

cgillum commented Jan 9, 2025

@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?

@YunchuWang YunchuWang requested a review from cgillum January 9, 2025 10:05
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
Microsoft.DurableTask.sln Outdated Show resolved Hide resolved
src/Extensions/Azure/Azure.csproj Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerOptions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerExtensions.cs Outdated Show resolved Hide resolved
src/Extensions/Azure/DurableTaskSchedulerOptions.cs Outdated Show resolved Hide resolved
@jviau
Copy link
Member

jviau commented Jan 10, 2025

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?

I wonder if we want a name other that Extensions for backends? I am worried this may get confused with a backend vs helpers for Azure / a specific Azure service. Say we wanted to port an AzureStorage backend hook to durable - where would that go? Or if we wanted to introduce helpers (not backends) for interacting with Azure services (Compute, Storage, CosmosDB, etc). Where would those go?

There is the Worker and Client naming for this already. Worker's are backends, Clients are for interacting with a backend. Would separate Worker.AzureManaged and Client.AzureManaged (or just "Azure") be better?

This also brings up the point of keeping a separate Worker and Client package. While it is more setup work on our end, the benefit is customers can bring in just the clients for their API projects and not need the entire worker.

@YunchuWang YunchuWang requested a review from jviau January 11, 2025 21:48
@YunchuWang
Copy link
Member Author

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?

I wonder if we want a name other that Extensions for backends? I am worried this may get confused with a backend vs helpers for Azure / a specific Azure service. Say we wanted to port an AzureStorage backend hook to durable - where would that go? Or if we wanted to introduce helpers (not backends) for interacting with Azure services (Compute, Storage, CosmosDB, etc). Where would those go?

There is the Worker and Client naming for this already. Worker's are backends, Clients are for interacting with a backend. Would separate Worker.AzureManaged and Client.AzureManaged (or just "Azure") be better?

This also brings up the point of keeping a separate Worker and Client package. While it is more setup work on our end, the benefit is customers can bring in just the clients for their API projects and not need the entire worker.

any advice on jacob's comment @cgillum ?

@YunchuWang
Copy link
Member Author

@jviau thanks for all the comments! i updated the pr and its ready for another review, added more tests and tested it manually with sample and working

@cgillum
Copy link
Member

cgillum commented Jan 12, 2025

There is the Worker and Client naming for this already. Worker's are backends, Clients are for interacting with a backend. Would separate Worker.AzureManaged and Client.AzureManaged (or just "Azure") be better?

This also brings up the point of keeping a separate Worker and Client package. While it is more setup work on our end, the benefit is customers can bring in just the clients for their API projects and not need the entire worker.

@jviau this makes sense to me. How about this - there will be two new nuget packages:

  • Microsoft.DurableTask.Client.AzureManaged
  • Microsoft.DurableTask.Worker.AzureManaged

I prefer "AzureManaged" over "Azure" because "Azure" becomes ambiguous if we ever wanted to introduce "AzureStorage" or something similar as a backend option for this SDK in the future.

@YunchuWang in terms of project structure, how about this?

  • /src/Client/AzureManaged/Client.AzureManaged.csproj
  • /src/Worker/AzureManaged/Worker.AzureManaged.csproj
  • /src/Shared/AzureManaged/*.cs

The /src/Shared/AzureManaged directory would just be shared .cs files that get imported into both projects, similar to /src/Shared/Grpc.

@YunchuWang
Copy link
Member Author

YunchuWang commented Jan 12, 2025

split into separate packages and split tests, also add @cgillum 's sample to here using split packages and tested working
{9C0F3874-3AD9-459E-AF9F-4A643215CF8A}, thanks, can you review again?

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should not be a shared project. Rather, we should important the code directly into the both the client and worker projects. The other directories under /src/Shared as examples.

The reason for this is that we don't want to have to publish and maintain a "Shared" nuget package.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct - move these files to src/Shared/AzureManaged, then include them via:

    <SharedSection Include="AzureManaged" />

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some public types in the AzureManaged shared files right now, which we want to avoid. Options are:

  1. Introduce another package Microsoft.DurableTask.Abstractions.AzureManaged to contain the common files.
    • this makes 3 packages for 1 feature. Once set up it isn't that hard to maintain, but I would understand if there is pushback on it being cumbersome to need 3 packages per backend.
  2. Consolidate all the packages into 1, Microsoft.DurableTask.Backend.AzureManaged or Microsoft.DurableTask.AzureManaged

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like number 2, 3 packages seem a bit cumbersome, basically we do a rename and go back to original approach before/ @cgillum for advice

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed with Chris offline, he suggested to keep two packages and use differently named option types to separate shared ones

samples/AspNetWebApp/AspNetWebApp.csproj Outdated Show resolved Hide resolved
samples/AspNetWebApp/Program.cs Outdated Show resolved Hide resolved
src/Shared/AzureManaged/DurableTaskSchedulerOptions.cs Outdated Show resolved Hide resolved
src/Shared/AzureManaged/DurableTaskSchedulerOptions.cs Outdated Show resolved Hide resolved
samples/AspNetWebApp/Utils.cs Outdated Show resolved Hide resolved
samples/AspNetWebApp/appsettings.Production.json Outdated Show resolved Hide resolved
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some public types in the AzureManaged shared files right now, which we want to avoid. Options are:

  1. Introduce another package Microsoft.DurableTask.Abstractions.AzureManaged to contain the common files.
    • this makes 3 packages for 1 feature. Once set up it isn't that hard to maintain, but I would understand if there is pushback on it being cumbersome to need 3 packages per backend.
  2. Consolidate all the packages into 1, Microsoft.DurableTask.Backend.AzureManaged or Microsoft.DurableTask.AzureManaged

src/Shared/AzureManaged/Shared.AzureManaged.csproj Outdated Show resolved Hide resolved
@YunchuWang YunchuWang requested a review from cgillum January 13, 2025 22:44
@YunchuWang YunchuWang requested a review from jviau January 14, 2025 05:02
@YunchuWang
Copy link
Member Author

YunchuWang commented Jan 14, 2025

thanks, updated to two packages. can i get another review @jviau @cgillum

Copy link
Member

@cgillum cgillum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few minor things.

- name: Setup .NET 6.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this step needed? I would expect that the step below would take care of everything?

/// using the provided Durable Task Scheduler options.
/// </summary>
/// <param name="schedulerOptions">Monitor for accessing the current scheduler options configuration.</param>
internal class ConfigureGrpcChannel(IOptionsMonitor<DurableTaskSchedulerClientOptions> schedulerOptions) :
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a private class?

Suggested change
internal class ConfigureGrpcChannel(IOptionsMonitor<DurableTaskSchedulerClientOptions> schedulerOptions) :
class ConfigureGrpcChannel(IOptionsMonitor<DurableTaskSchedulerClientOptions> schedulerOptions) :

/// Gets or sets the worker ID used to identify the worker instance.
/// The default value is a string containing the machine name, process ID, and a unique identifier.
/// </summary>
public string WorkerId { get; set; } = $"{Environment.MachineName},{Environment.ProcessId},{Guid.NewGuid():N}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client options don't need a worker ID. We only need this for Worker options.


/// <summary>
/// Gets or sets a value indicating whether to allow insecure channel credentials.
/// This should only be set to true in development/testing scenarios.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// This should only be set to true in development/testing scenarios.
/// This should only be set to true in local development/testing scenarios.

string authType = connectionString.Authentication;

// Parse the supported auth types, in a case-insensitive way and without spaces
switch (authType.ToLower(CultureInfo.InvariantCulture).Replace(" ", string.Empty))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not allow spaces. I'd like to reduce the test matrix that we have to worry about for all language SDKs. Also, I think it's more natural to use .ToLowerInvariant() instead of .ToLower(CultureInfo.Invariant)

Suggested change
switch (authType.ToLower(CultureInfo.InvariantCulture).Replace(" ", string.Empty))
switch (authType.ToLowerInvariant)


public class DurableTaskSchedulerClientExtensionsTests
{
private const string ValidEndpoint = "myaccount.westus3.durabletask.io";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: please update the code in these files to match the C# style used by this repo. Your editor should be getting hints about this because of the .editorconfig file that exists in this repo. In particular, we don't use default visibility modifiers (like private here) and we generally don't use var.

// Assert
var provider = services.BuildServiceProvider();
var options = provider.GetService<IOptions<GrpcDurableTaskClientOptions>>();
options.Should().NotBeNull();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why we don't validate the properties on the options in this test?


// Assert
action.Should().Throw<ArgumentNullException>()
.WithMessage("Value cannot be null. (Parameter 'The connection string is missing the required 'Endpoint' property.')");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look correct. It should be Parameter '{parameterName}' but here you have it showing the message. Are we constructing exceptions incorrectly in the validation code?


// Act & Assert
var action = () => mockBuilder.Object.UseDurableTaskScheduler(connectionString);
action.Should().Throw<ArgumentException>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct exception to throw in this case is ArgumentNullException.

};

// Act
var channel = options.CreateChannel();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we dispose this channel to avoid leaking resources?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants