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

Dapr azure hosting ext #371

Merged

Conversation

FullStackChef
Copy link
Contributor

@FullStackChef FullStackChef commented Jan 6, 2025

Closes #349

  • Created CommunityToolkit.Aspire.Hosting.Dapr.AzureExtensions
    • Provides functionality for referencing azure resources for dapr components
  • Created CommunityToolkit.Aspire.Hosting.Dapr.AzureRedis
    • Provides extensions for referencing azure redis for dapr state

PR Checklist

  • Created a feature/dev branch in your fork (vs. submitting directly from a commit on main)
  • Based off latest main branch of toolkit
  • PR doesn't include merge commits (always rebase on top of our main, if needed)
  • New integration
    • Docs are written
    • Added description of major feature to project description for NuGet package (4000 total character limit, so don't push entire description over that)
  • Tests for the changes have been added (for bug fixes / features) (if applicable)
  • Contains NO breaking changes
  • Every new API (including internal ones) has full XML docs
  • Code follows all style conventions

Other information

Create Dapr Azure Redis project
Create Example AppHost + ApiService
Create Dapr resource for provisioning
Add AzureDaprComponentResource
Start of unit tests
Fixes based on unit tests
Comment on lines 17 to 21
2. **`ConfigureRedisStateComponent`**
Internal helper that creates and configures a “state.redis” Dapr component based on the `AzureRedisCacheResource`.
- Generates a valid hostname and port.
- Supports Azure Entra ID (AadEnabled) for secure access.
- Stores or references secrets in Azure Key Vault if password-based authentication is required.

Choose a reason for hiding this comment

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

Not sure internal details need to be in the readme

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have adjusted docs

Copy link
Member

@aaronpowell aaronpowell left a comment

Choose a reason for hiding this comment

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

As I was doing the review, it made me wonder if it wouldn't make more sense to create a single package, CommunityToolkit.Aspire.Hosting.Dapr.Azure.

My understanding is that the AzureExtensions package doesn't really do anything on its own, you'd need to bring in a package such as the Dapr.AzureRedis one or you'd create your own using some other Azure service to act as the caching layer in Azure.

Is the risk that if you aren't wanting to use Azure Redis as the caching layer you're going to end up bringing in some dependencies that are unneeded to the application (and thus "bloat")?

I guess I don't know enough about Dapr and what's needed/how you go about hosting it in Azure to truly understand what alternatives there might need to be in the future.

@@ -22,7 +22,6 @@

<Target Name="PublishRunMaven" AfterTargets="Build">
<!-- As part of publishing, ensure the Java app is freshly built -->
<Exec WorkingDirectory="$(JavaAppRoot)" Command="./mvnw --quiet clean package" />
Copy link
Member

Choose a reason for hiding this comment

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

Can you revert this change, as we have a PR to resolve it (and it'll cause builds to fail if it's not included at the moment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done - I'm pretty sure I didn't intentionally remove this.

public static IResourceBuilder<AzureDaprComponentResource> AddAzureDaprResource(
this IResourceBuilder<IDaprComponentResource> builder,
[ResourceName] string name,
Action<AzureResourceInfrastructure> configureInfrastructure)
Copy link
Member

Choose a reason for hiding this comment

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

This parameter is non-null and thus required, is that intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it is intended and required because it is necessary

Copy link
Member

Choose a reason for hiding this comment

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

Is someone always going to need to provide their own configuration callback? Could it not be nullable and we have a default (even if it's just a noop)?

Or is it more that these are methods that you wouldn't realistically use in isolation, you'd only use them through wrappers like the Dapr Azure Redis package?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I mean you could no-op it, but I don't see a use case where you would use it without configuration.

It is primarily designed to be used by the wrappers - and I'm sitting here arguing with myself whether you would realistically use it in isolation.

On one hand if a wrapped integration exists for the component you're wanting to use - it makes sense to use that, however if not this integration avoids the pain I experienced trying to figure it out.

I think provided that I (or someone) can contribute wrapped version in a timely manner then this library could in fact be a supporting library that wasn't published separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The more I think about this the more I think that this shouldn't be published on it's own and should only serve as the base for more specific integrations.

@FullStackChef
Copy link
Contributor Author

As I was doing the review, it made me wonder if it wouldn't make more sense to create a single package, CommunityToolkit.Aspire.Hosting.Dapr.Azure.

My understanding is that the AzureExtensions package doesn't really do anything on its own, you'd need to bring in a package such as the Dapr.AzureRedis one or you'd create your own using some other Azure service to act as the caching layer in Azure.

Is the risk that if you aren't wanting to use Azure Redis as the caching layer you're going to end up bringing in some dependencies that are unneeded to the application (and thus "bloat")?

I guess I don't know enough about Dapr and what's needed/how you go about hosting it in Azure to truly understand what alternatives there might need to be in the future.

@aaronpowell yeah it would be a pretty bloated integration. Dapr has multiple "building blocks" - state is just one of these building blocks can be used with redis, cosmosdb, postgres, table / blob storage or sql server (to name the azure ones). so you would end up referencing a rather large number of the aspire hosting packages. which felt like it is contra to the design of aspire.

public static IResourceBuilder<AzureDaprComponentResource> AddAzureDaprResource(
this IResourceBuilder<IDaprComponentResource> builder,
[ResourceName] string name,
Action<AzureResourceInfrastructure> configureInfrastructure)
Copy link
Member

Choose a reason for hiding this comment

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

Is someone always going to need to provide their own configuration callback? Could it not be nullable and we have a default (even if it's just a noop)?

Or is it more that these are methods that you wouldn't realistically use in isolation, you'd only use them through wrappers like the Dapr Azure Redis package?

@aaronpowell
Copy link
Member

I'm wondering if the Azure Extensions package should ship as a source-only package (see https://andrewlock.net/creating-source-only-nuget-packages/) so that there's one less binary people would end up with, as they'd compile in the stuff they need (also means things could be marked as internal for library authors).

Copy link
Member

@aaronpowell aaronpowell left a comment

Choose a reason for hiding this comment

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

I think this looks good - anything else outstanding in your opinion @FullStackChef

@FullStackChef FullStackChef changed the base branch from main to dapr-migration January 15, 2025 18:52
remove Dapr Azure extensions project
make dapr azure extensions internal
include extensions in dapr redis package
include extensions in dapr extensions tests
make dapr redis internals visible to dapr redis tests
@FullStackChef FullStackChef marked this pull request as ready for review January 15, 2025 19:32
@FullStackChef
Copy link
Contributor Author

@aaronpowell I've made the following changes per our conversation

  • updated the PR to target your dapr-migration branch
  • remove Dapr Azure extensions project
  • make dapr azure extensions internal
  • include extensions in dapr redis package
  • include extensions in dapr extensions tests
  • make dapr redis internals visible to dapr redis tests

I haven't yet removed the references to the existing dapr extension as it will mean my branch doesn't build and that feels wrong. Hoping we can do this as a cleanup task prior to merging dapr-migration?

Other than that - my src, samples and tests all build and the tests run successfully.

Let me know how you want to proceed.

@aaronpowell
Copy link
Member

Yep, let's do it as cleanup once merged in

Value = BicepFunction.Interpolate($"{redisCacheResource.HostName}:{port}")
});

daprResourceBuilder.WithParameter("redisHost", source.GetOutput("daprConnectionString"));
Copy link

@davidfowl davidfowl Jan 16, 2025

Choose a reason for hiding this comment

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

Same comment as below, do this outside of the callback


var daprResourceBuilder = builder.AddAzureDaprResource(redisDaprState, configureInfrastructure);

source.ConfigureInfrastructure(redisCache =>

Choose a reason for hiding this comment

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

Conventionally this parameter is "infra" as it refers to the bicep module

Copy link
Contributor Author

Choose a reason for hiding this comment

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

makes sense - will change

else
{
redisCache.ConfigureSecretAccess(daprComponent, redisCacheResource);
daprResourceBuilder.WithParameter("redisPasswordSecretUri", source.GetOutput("redisPasswordSecretUri"));

Choose a reason for hiding this comment

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

I wouldn't have this inside of the ConfigureInfrastructure callback (don't cross the streams).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@davidfowl I appreciate the ghostbusters ref, but I'm not sure how else to do this and I'm not really clear why?

This is being added conditionally as a result of something that happens inside ConfigureInfrastructure (checking if the redis has entra Id set) I could use a variable, but I don't know that that actually changes anything other than abstracting it away from view.

Choose a reason for hiding this comment

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

You want to think about where these callbacks run, what state is captured in them and what values you are referencing. It's not just C# code running that can all comingle, the lifetime and objects you use matter in these callbacks (were compiling to bicep) 😄 .

Treat that callback as producing the bicep module. It runs super late just before we are about to write the bicep to disk. You don't want to set parameters that late right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm stuck sorry, In theory I understand what you're saying,

I just don't see how I can conditionally add this variable outside of configure infrastructure when AD authentication is set inside of configure infrastructure and if you call WithAccessKeyAuthentication then both the removal of AD and the addition of keyvault happens inside of ConfigureInfrastructure

I feel like this would be achievable if more of the conditional attributes made use of annotations

Choose a reason for hiding this comment

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

Why is this line hard to move out of the callback:

daprResourceBuilder.WithParameter("redisPasswordSecretUri", source.GetOutput("redisPasswordSecretUri"));

Copy link
Contributor Author

Choose a reason for hiding this comment

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

because that line is conditional. It is only called if someone has explicitly disabled AD authentication.
From aspire

var redisState = builder.AddAzureRedis("redisState")
    .WithAccessKeyAuthentication() // This calls ConfigureInfrastructure to disable AD auth
    .RunAsContainer();

WithAccessKeyAuthentication() calls

redis.RedisConfiguration.IsAadEnabled.ClearValue();
redis.IsAccessKeyAuthenticationDisabled.ClearValue();

and sets up key vault infra for the redis

If I move that line outside of the call back, the parameter is added unconditionally

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

Feel free to leave in the hack for now (consider using private reflection)

@aaronpowell
Copy link
Member

As long as @davidfowl is happy with the changes, I reckon we're good to merge this in now

@aaronpowell aaronpowell merged commit 3c8d42f into CommunityToolkit:dapr-migration Jan 20, 2025
2 checks passed
aaronpowell added a commit that referenced this pull request Jan 31, 2025
* Dapr migration initial import (#378)

* Dropping files from Aspire repo

* Moving to the right namespace/folder naming

* Adding to solution and getting it to compile

* Adding in the Dapr tests from the Aspire repo

Had to remove the DaprSchemaTests file as we can't do schema tests (missing a lot of infrastructure from the Aspire repo).

Had to edit the DaprTests to not use EnvironmentVariableEvaluator, which we can't leverage as it uses some internal types from Aspire.Hosting. This means that our testing of the environment variables is slightly different, and the values we assert against are not the docker internal host endpoints, but the public endpoints

* Dapr azure hosting ext (#371)

* Create Dap Azure extensions project
Create Dapr Azure Redis project
Create Example AppHost + ApiService

* Work in progress
Create Dapr resource for provisioning

* Remove specific resource as not generating properly
Use AddAzureInfrastructure

* Updated to use secret refs

* Fix - Remove code used for testing
Add AzureDaprComponentResource
Start of unit tests

* Unit tests +
Fixes based on unit tests

* remove bicep file

* Tests for AzureRedis

* Add Readme and perform small cleanup tasks

* Update src/CommunityToolkit.Aspire.Hosting.Dapr.AzureExtensions/ApplicationModel/AzureDaprComponentResource.cs

Co-authored-by: Aaron Powell <[email protected]>

* Apply suggestions from code review

Co-authored-by: Aaron Powell <[email protected]>

* Revert unintentional change to Java.AppHost

* Update tests/CommunityToolkit.Aspire.Hosting.Dapr.AzureRedis.Tests/ResourceCreationTests.cs

Co-authored-by: Aaron Powell <[email protected]>

* Updated azure redis documentation also added missing xmldocs for api

* correct unit test approach

* remove unnecessary comment

* null checking

* More null checking

* Update Azure redis readme

* Update AzureExtension readme

* move extensions to shared files
remove Dapr Azure extensions project
make dapr azure extensions internal
include extensions in dapr redis package
include extensions in dapr extensions tests
make dapr redis internals visible to dapr redis tests

* change redisCache to infra rename source to redisBuilder

---------

Co-authored-by: Aaron Powell <[email protected]>

* Fixing broken sln

* Dapr example and tests (#394)

* Migrating sample from dotnet/aspire repo

* Adding dapr tests

* Adding dapr setup to CI workflow

* Following the setup-dapr instructions

* Debugging CI

* Falling back to looking at PATH

* Fixing line endings replacement

* Added PATH lookup to Windows

* Adding some diagnostic info

* Changing log level

* init was only running on linux, which I think is wrong

* Turning up logging

* Bypass logging

* Interpolated strings

* Adding resource logger service

* Java app build extension (#348)

* Adding the ability to do a maven build in the app host

This uses an event to run the mvnw build (or it can be customised via options), so it is only used when the app host is running the resource

Fixes #339

* Adding a 'build with maven' command

Allows you to rebuild the java app without having to restart the whole app host.

* Expanding test coverage

* Some more tests

* Windows exe needs a file extension

* Rolling back some changes

* Renaming step

* Tidying up the tests

* Requiring docker for dapr tests

* Adding codeowners

* Moving dapr extensions to use our dapr package

* Forgot to update the tests project

* Adding some assembly filters

* Adding readme updates

* Slight break in the markdown

---------

Co-authored-by: Brett Smith <[email protected]>
@FullStackChef FullStackChef deleted the dapr-azure-hosting-ext branch February 1, 2025 20:50
@Alirexaa Alirexaa added this to the 9.2 milestone Feb 6, 2025
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.

[Feature] Dapr - Azure extensions + Redis configuration
4 participants