-
Notifications
You must be signed in to change notification settings - Fork 769
Add a builder pattern for resolving resource configuration #13221
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
Merged
danegsta
merged 22 commits into
dotnet:main
from
danegsta:danegsta/referenceTrackingValueProvider
Dec 4, 2025
Merged
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
0cf7f52
Add a builder pattern for resolving resource configuration
danegsta 80ef95c
Update services available to test
danegsta 2211dc8
Make configuration gatherer implementations internal
danegsta badbfef
Apply suggestion from @Copilot
danegsta a1fcffc
Remove unused code
danegsta a140660
Update src/Aspire.Hosting/ApplicationModel/ResourceServerAuthenticati…
danegsta 10ce666
Update src/Aspire.Hosting/ApplicationModel/ResourceCertificateTrustCo…
danegsta dc3a8d0
Update src/Aspire.Hosting/ApplicationModel/ResourceServerAuthenticati…
danegsta 1e1a448
Update src/Aspire.Hosting/ApplicationModel/ResourceConfigurationGathe…
danegsta 4e48098
Update doc comment
danegsta 1a487e3
Add missing bracket
danegsta b90751d
Add test cases for new configuration gatherer implmentations
danegsta 4d4b8c7
Fix missing paramater
danegsta 2bce908
Rename Configuration to ExecutionConfiguration
danegsta ffdcc62
Merge remote-tracking branch 'upstream/main' into danegsta/referenceT…
danegsta 9fae909
Convert resource evaluation to new bulder pattern
danegsta 7b22da7
Fix failing tests after update
danegsta 6c07355
Check for inner exception
danegsta 16c2035
Switch to returning a tuple
danegsta c866b9b
Missed one last place to update the tuple
danegsta 4d3c949
Add an extension method to retrieve the builder directly from a resource
danegsta e14e6ff
Handle empty create file results
danegsta File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
src/Aspire.Hosting/ApplicationModel/IResourceConfiguration.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Configuration (arguments and environment variables) to apply to a specific resource. | ||
| /// </summary> | ||
| public interface IResourceConfiguration | ||
| { | ||
| /// <summary> | ||
| /// Gets the arguments to apply to the resource. | ||
| /// </summary> | ||
| IReadOnlyList<(string Value, bool IsSensitive)> Arguments { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the environment variables to apply to the resource. | ||
| /// </summary> | ||
| IReadOnlyDictionary<string, string> EnvironmentVariables { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the metadata associated with the resource configuration. | ||
| /// </summary> | ||
| IReadOnlySet<IResourceConfigurationMetadata> Metadata { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the exception that occurred while gathering the resource configuration, if any. | ||
| /// If multiple exceptions occurred, they are aggregated into an AggregateException. | ||
| /// </summary> | ||
| Exception? Exception { get; } | ||
| } | ||
25 changes: 25 additions & 0 deletions
25
src/Aspire.Hosting/ApplicationModel/IResourceConfigurationBuilder.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Builder for producing the configuration (arguments and environment variables) to apply to a specific resource. | ||
| /// </summary> | ||
| public interface IResourceConfigurationBuilder | ||
| { | ||
| /// <summary> | ||
| /// Adds a configuration gatherer to the builder. | ||
| /// </summary> | ||
| /// <param name="gatherer">The configuration gatherer to add.</param> | ||
| /// <returns>The current instance of the builder.</returns> | ||
| IResourceConfigurationBuilder AddConfigurationGatherer(IResourceConfigurationGatherer gatherer); | ||
|
|
||
| /// <summary> | ||
| /// Builds the resource configuration. | ||
| /// </summary> | ||
| /// <param name="executionContext">The execution context.</param> | ||
| /// <param name="cancellationToken">A cancellation token.</param> | ||
| /// <returns>The resource configuration.</returns> | ||
| Task<IResourceConfiguration> BuildAsync(DistributedApplicationExecutionContext executionContext, CancellationToken cancellationToken = default); | ||
| } |
19 changes: 19 additions & 0 deletions
19
src/Aspire.Hosting/ApplicationModel/IResourceConfigurationGatherer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Gathers resource configurations (arguments and environment variables) and optionally | ||
| /// applies additional metadata to the resource. | ||
| /// </summary> | ||
| public interface IResourceConfigurationGatherer | ||
| { | ||
| /// <summary> | ||
| /// Gathers the relevant resource configuration. | ||
| /// </summary> | ||
| /// <param name="context">The initial resource configuration context.</param> | ||
| /// <param name="cancellationToken">A cancellation token.</param> | ||
| /// <returns>A task representing the asynchronous operation.</returns> | ||
| ValueTask GatherAsync(IResourceConfigurationGathererContext context, CancellationToken cancellationToken = default); | ||
danegsta marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
43 changes: 43 additions & 0 deletions
43
src/Aspire.Hosting/ApplicationModel/IResourceConfigurationGathererContext.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Resource configuration gatherer context. | ||
| /// </summary> | ||
| public interface IResourceConfigurationGathererContext | ||
| { | ||
| /// <summary> | ||
| /// The resource for which configuration is being gathered. | ||
| /// </summary> | ||
| IResource Resource { get; } | ||
|
|
||
| /// <summary> | ||
| /// The logger for the resource. | ||
| /// </summary> | ||
| ILogger ResourceLogger { get; } | ||
|
|
||
| /// <summary> | ||
| /// The execution context in which the resource is being configured. | ||
| /// </summary> | ||
| DistributedApplicationExecutionContext ExecutionContext { get; } | ||
|
|
||
| /// <summary> | ||
| /// Collection of resource command line arguments. | ||
| /// </summary> | ||
| List<object> Arguments { get; } | ||
|
|
||
| /// <summary> | ||
| /// Collection of resource environment variables. | ||
| /// </summary> | ||
| Dictionary<string, object> EnvironmentVariables { get; } | ||
|
|
||
| /// <summary> | ||
| /// Adds metadata associated with the resource configuration. | ||
| /// </summary> | ||
| /// <param name="metadata">The metadata to add.</param> | ||
| void AddMetadata(IResourceConfigurationMetadata metadata); | ||
| } |
11 changes: 11 additions & 0 deletions
11
src/Aspire.Hosting/ApplicationModel/IResourceConfigurationMetadata.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Metadata associated with a resource configuration. | ||
| /// </summary> | ||
| public interface IResourceConfigurationMetadata | ||
| { | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
src/Aspire.Hosting/ApplicationModel/ResourceArgumentsConfigurationGatherer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Gathers command line arguments for resources. | ||
| /// </summary> | ||
| internal class ResourceArgumentsConfigurationGatherer : IResourceConfigurationGatherer | ||
| { | ||
| /// <inheritdoc/> | ||
| public async ValueTask GatherAsync(IResourceConfigurationGathererContext context, CancellationToken cancellationToken = default) | ||
| { | ||
| if (context.Resource.TryGetAnnotationsOfType<CommandLineArgsCallbackAnnotation>(out var callbacks)) | ||
| { | ||
| var callbackContext = new CommandLineArgsCallbackContext(context.Arguments, context.Resource, cancellationToken) | ||
| { | ||
| Logger = context.ResourceLogger, | ||
| ExecutionContext = context.ExecutionContext | ||
| }; | ||
|
|
||
| foreach (var callback in callbacks) | ||
| { | ||
| await callback.Callback(callbackContext).ConfigureAwait(false); | ||
| } | ||
| } | ||
| } | ||
| } |
149 changes: 149 additions & 0 deletions
149
src/Aspire.Hosting/ApplicationModel/ResourceCertificateTrustConfigurationGatherer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| #pragma warning disable ASPIRECERTIFICATES001 | ||
|
|
||
| using System.Security.Cryptography.X509Certificates; | ||
| using Aspire.Hosting.Utils; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Gathers certificate trust configuration for resources that require it. | ||
| /// </summary> | ||
| internal class ResourceCertificateTrustConfigurationGatherer : IResourceConfigurationGatherer | ||
| { | ||
| private readonly Func<CertificateTrustScope, CertificateTrustConfigurationContext> _configContextFactory; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of <see cref="ResourceCertificateTrustConfigurationGatherer"/>. | ||
| /// </summary> | ||
| /// <param name="configContextFactory">A factory for configuring certificate trust configuration properties.</param> | ||
| public ResourceCertificateTrustConfigurationGatherer(Func<CertificateTrustScope, CertificateTrustConfigurationContext> configContextFactory) | ||
| { | ||
| _configContextFactory = configContextFactory; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public async ValueTask GatherAsync(IResourceConfigurationGathererContext context, CancellationToken cancellationToken = default) | ||
| { | ||
| var developerCertificateService = context.ExecutionContext.ServiceProvider.GetRequiredService<IDeveloperCertificateService>(); | ||
| var trustDevCert = developerCertificateService.TrustCertificate; | ||
|
|
||
| // Add additional certificate trust configuration metadata | ||
| var metadata = new CertificateTrustConfigurationMetadata(); | ||
| context.AddMetadata(metadata); | ||
|
|
||
| metadata.Scope = CertificateTrustScope.Append; | ||
| var certificates = new X509Certificate2Collection(); | ||
| if (context.Resource.TryGetLastAnnotation<CertificateAuthorityCollectionAnnotation>(out var caAnnotation)) | ||
| { | ||
| foreach (var certCollection in caAnnotation.CertificateAuthorityCollections) | ||
| { | ||
| certificates.AddRange(certCollection.Certificates); | ||
| } | ||
|
|
||
| trustDevCert = caAnnotation.TrustDeveloperCertificates.GetValueOrDefault(trustDevCert); | ||
| metadata.Scope = caAnnotation.Scope.GetValueOrDefault(metadata.Scope); | ||
| } | ||
|
|
||
| if (metadata.Scope == CertificateTrustScope.None) | ||
| { | ||
| // No certificate trust configuration to apply | ||
| return; | ||
| } | ||
|
|
||
| if (metadata.Scope == CertificateTrustScope.System) | ||
| { | ||
| // Read the system root certificates and add them to the collection | ||
| certificates.AddRootCertificates(); | ||
| } | ||
|
|
||
| if (context.ExecutionContext.IsRunMode && trustDevCert) | ||
| { | ||
| foreach (var cert in developerCertificateService.Certificates) | ||
| { | ||
| certificates.Add(cert); | ||
| } | ||
| } | ||
|
|
||
| metadata.Certificates.AddRange(certificates); | ||
|
|
||
| if (!metadata.Certificates.Any()) | ||
| { | ||
| // No certificates to configure | ||
| context.ResourceLogger.LogInformation("No custom certificate authorities to configure for '{ResourceName}'. Default certificate authority trust behavior will be used.", context.Resource.Name); | ||
| return; | ||
| } | ||
|
|
||
| var configurationContext = _configContextFactory(metadata.Scope); | ||
|
|
||
| // Apply default OpenSSL environment configuration for certificate trust | ||
| context.EnvironmentVariables["SSL_CERT_DIR"] = configurationContext.CertificateDirectoriesPath; | ||
|
|
||
| if (metadata.Scope != CertificateTrustScope.Append) | ||
| { | ||
| context.EnvironmentVariables["SSL_CERT_FILE"] = configurationContext.CertificateBundlePath; | ||
| } | ||
|
|
||
| var callbackContext = new CertificateTrustConfigurationCallbackAnnotationContext | ||
| { | ||
| ExecutionContext = context.ExecutionContext, | ||
| Resource = context.Resource, | ||
| Scope = metadata.Scope, | ||
| CertificateBundlePath = configurationContext.CertificateBundlePath, | ||
| CertificateDirectoriesPath = configurationContext.CertificateDirectoriesPath, | ||
| Arguments = context.Arguments, | ||
| EnvironmentVariables = context.EnvironmentVariables, | ||
| CancellationToken = cancellationToken, | ||
| }; | ||
|
|
||
| if (context.Resource.TryGetAnnotationsOfType<CertificateTrustConfigurationCallbackAnnotation>(out var callbacks)) | ||
| { | ||
| foreach (var callback in callbacks) | ||
| { | ||
| await callback.Callback(callbackContext).ConfigureAwait(false); | ||
| } | ||
| } | ||
|
|
||
| if (metadata.Scope == CertificateTrustScope.System) | ||
| { | ||
| context.ResourceLogger.LogInformation("Resource '{ResourceName}' has a certificate trust scope of '{Scope}'. Automatically including system root certificates in the trusted configuration.", context.Resource.Name, Enum.GetName(metadata.Scope)); | ||
| } | ||
|
|
||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Metadata about the resource certificate trust configuration. | ||
| /// </summary> | ||
| public class CertificateTrustConfigurationMetadata : IResourceConfigurationMetadata | ||
| { | ||
| /// <summary> | ||
| /// The certificate trust scope for the resource. | ||
| /// </summary> | ||
| public CertificateTrustScope Scope { get; internal set; } | ||
|
|
||
| /// <summary> | ||
| /// The collection of certificates to trust. | ||
| /// </summary> | ||
| public X509Certificate2Collection Certificates { get; } = new(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Context for configuring certificate trust configuration properties. | ||
| /// </summary> | ||
| public class CertificateTrustConfigurationContext | ||
| { | ||
| /// <summary> | ||
| /// The path to the certificate bundle file in the resource context (e.g., container filesystem). | ||
| /// </summary> | ||
| public required ReferenceExpression CertificateBundlePath { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// The path(s) to the certificate directories in the resource context (e.g., container filesystem). | ||
| /// </summary> | ||
| public required ReferenceExpression CertificateDirectoriesPath { get; init; } | ||
| } |
22 changes: 22 additions & 0 deletions
22
src/Aspire.Hosting/ApplicationModel/ResourceConfiguration.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Aspire.Hosting.ApplicationModel; | ||
|
|
||
| /// <summary> | ||
| /// Represents the configuration (arguments and environment variables) to apply to a specific resource. | ||
| /// </summary> | ||
| internal class ResourceConfiguration : IResourceConfiguration | ||
| { | ||
| /// <inheritdoc/> | ||
| public required IReadOnlyList<(string Value, bool IsSensitive)> Arguments { get; init; } | ||
|
|
||
| /// <inheritdoc/> | ||
| public required IReadOnlyDictionary<string, string> EnvironmentVariables { get; init; } | ||
|
|
||
| /// <inheritdoc/> | ||
| public required IReadOnlySet<IResourceConfigurationMetadata> Metadata { get; init; } | ||
|
|
||
| /// <inheritdoc/> | ||
| public required Exception? Exception { get; init; } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.