-
Notifications
You must be signed in to change notification settings - Fork 744
Add new event for finalizing resource annotations and use to configure installer certificate trust #13200
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
base: main
Are you sure you want to change the base?
Add new event for finalizing resource annotations and use to configure installer certificate trust #13200
Changes from all commits
f679c22
c680552
b939863
a659202
cf989d0
8f8ea2d
b15acfc
63c4f9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| // 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> | ||
| /// Annotation to register a callback to be invoked during resource finalization. Callbacks are executed in reverse order | ||
| /// of their registration immediately after the BeforeStartEvent is complete. | ||
| /// </summary> | ||
| public sealed class FinalizeResourceConfigurationCallbackAnnotation : IResourceAnnotation | ||
| { | ||
| /// <summary> | ||
| /// The callback to be invoked during resource finalization. | ||
| /// </summary> | ||
| public required Func<FinalizeResourceConfigurationCallbackAnnotationContext, Task> Callback { get; init; } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Context for a finalize resource configuration callback annotation. | ||
| /// </summary> | ||
|
Comment on lines
+18
to
+20
|
||
| public sealed class FinalizeResourceConfigurationCallbackAnnotationContext | ||
| { | ||
| /// <summary> | ||
| /// The resource associated with the callback. | ||
| /// </summary> | ||
| public required IResource Resource { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// The execution context for the callback. | ||
| /// </summary> | ||
| public required DistributedApplicationExecutionContext ExecutionContext { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// The cancellation token for the callback. | ||
| /// </summary> | ||
| public required CancellationToken CancellationToken { get; init; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3027,6 +3027,27 @@ public static IResourceBuilder<T> ExcludeFromMcp<T>(this IResourceBuilder<T> bui | |
| return builder.WithAnnotation(new ExcludeFromMcpAnnotation()); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Adds a resource configuration finalizer callback annotation to the resource. | ||
| /// This is the last safe opportunity to modify resource annotations before configuration processing begins and provides an | ||
| /// opportunity to apply default behaviors based on the final resource configuration. | ||
| /// </summary> | ||
| /// <typeparam name="T">The resource type.</typeparam> | ||
| /// <param name="builder">The resource builder.</param> | ||
| /// <param name="callback">The callback to be invoked during resource finalization. All resource configuration finalizers will be invoked in reverse order of their registration immediately after the BeforeStartEvent is complete.</param> | ||
| /// <returns>The <see cref="IResourceBuilder{T}"/>.</returns> | ||
|
Comment on lines
+3030
to
+3038
|
||
| public static IResourceBuilder<T> WithConfigurationFinalizer<T>(this IResourceBuilder<T> builder, Func<FinalizeResourceConfigurationCallbackAnnotationContext, Task> callback) | ||
| where T : IResource | ||
| { | ||
| ArgumentNullException.ThrowIfNull(builder); | ||
| ArgumentNullException.ThrowIfNull(callback); | ||
|
|
||
| return builder.WithAnnotation(new FinalizeResourceConfigurationCallbackAnnotation | ||
| { | ||
| Callback = callback, | ||
| }, ResourceAnnotationMutationBehavior.Append); | ||
danegsta marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Adds a callback to configure container image push options for the resource. | ||
| /// </summary> | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -367,7 +367,7 @@ private static void AssertPythonCommandPath(string expectedVenvPath, string actu | |||||||||||||||||||||||||||||||||||||||||||||||
| var expectedCommand = OperatingSystem.IsWindows() | ||||||||||||||||||||||||||||||||||||||||||||||||
| ? Path.Join(expectedVenvPath, "Scripts", "python.exe") | ||||||||||||||||||||||||||||||||||||||||||||||||
| : Path.Join(expectedVenvPath, "bin", "python"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| Assert.Equal(expectedCommand, actualCommand); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -552,7 +552,7 @@ public void WithVirtualEnvironment_UsesAppHostDirectoryWhenVenvOnlyExistsThere() | |||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); | ||||||||||||||||||||||||||||||||||||||||||||||||
| using var tempAppDir = new TempDirectory(); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Create app directory as a subdirectory of AppHost (realistic scenario) | ||||||||||||||||||||||||||||||||||||||||||||||||
| var appDirName = "python-app"; | ||||||||||||||||||||||||||||||||||||||||||||||||
| var appDirPath = Path.Combine(builder.AppHostDirectory, appDirName); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -594,7 +594,7 @@ public void WithVirtualEnvironment_UsesAppHostDirectoryWhenVenvOnlyExistsThere() | |||||||||||||||||||||||||||||||||||||||||||||||
| public void WithVirtualEnvironment_PrefersAppDirectoryWhenVenvExistsInBoth() | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Create app directory as a subdirectory of AppHost (realistic scenario) | ||||||||||||||||||||||||||||||||||||||||||||||||
| var appDirName = "python-app"; | ||||||||||||||||||||||||||||||||||||||||||||||||
| var appDirPath = Path.Combine(builder.AppHostDirectory, appDirName); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -663,7 +663,7 @@ public void WithVirtualEnvironment_DefaultsToAppDirectoryWhenVenvExistsInNeither | |||||||||||||||||||||||||||||||||||||||||||||||
| public void WithVirtualEnvironment_ExplicitPath_UsesVerbatim() | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(outputHelper); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Create app directory as a subdirectory of AppHost | ||||||||||||||||||||||||||||||||||||||||||||||||
| var appDirName = "python-app"; | ||||||||||||||||||||||||||||||||||||||||||||||||
| var appDirPath = Path.Combine(builder.AppHostDirectory, appDirName); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -680,7 +680,7 @@ public void WithVirtualEnvironment_ExplicitPath_UsesVerbatim() | |||||||||||||||||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| var scriptName = "main.py"; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Explicitly specify a custom venv path - should use it verbatim, not fall back to AppHost .venv | ||||||||||||||||||||||||||||||||||||||||||||||||
| var resourceBuilder = builder.AddPythonApp("pythonProject", appDirName, scriptName) | ||||||||||||||||||||||||||||||||||||||||||||||||
| .WithVirtualEnvironment("custom-venv"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2297,6 +2297,26 @@ private static async Task PublishBeforeStartEventAsync(DistributedApplication ap | |||||||||||||||||||||||||||||||||||||||||||||||
| var appModel = app.Services.GetRequiredService<DistributedApplicationModel>(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| var eventing = app.Services.GetRequiredService<IDistributedApplicationEventing>(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| await eventing.PublishAsync(new BeforeStartEvent(app.Services, appModel), CancellationToken.None); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var resource in appModel.Resources) | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (resource.TryGetAnnotationsOfType<FinalizeResourceConfigurationCallbackAnnotation>(out var finalizeAnnotations)) | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| var context = new FinalizeResourceConfigurationCallbackAnnotationContext | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| Resource = resource, | ||||||||||||||||||||||||||||||||||||||||||||||||
| ExecutionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run), | ||||||||||||||||||||||||||||||||||||||||||||||||
| CancellationToken = CancellationToken.None, | ||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Execute in reverse order; take as a list to avoid mutating the collection during enumeration | ||||||||||||||||||||||||||||||||||||||||||||||||
| var callbacks = finalizeAnnotations.Reverse().ToList(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var callback in callbacks) | ||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||
| await callback.Callback(context).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2301
to
+2319
|
||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var resource in appModel.Resources) | |
| { | |
| if (resource.TryGetAnnotationsOfType<FinalizeResourceConfigurationCallbackAnnotation>(out var finalizeAnnotations)) | |
| { | |
| var context = new FinalizeResourceConfigurationCallbackAnnotationContext | |
| { | |
| Resource = resource, | |
| ExecutionContext = new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run), | |
| CancellationToken = CancellationToken.None, | |
| }; | |
| // Execute in reverse order; take as a list to avoid mutating the collection during enumeration | |
| var callbacks = finalizeAnnotations.Reverse().ToList(); | |
| foreach (var callback in callbacks) | |
| { | |
| await callback.Callback(context).ConfigureAwait(false); | |
| } | |
| } | |
| } | |
| await ResourceFinalizationHelper.ExecuteFinalizersAsync( | |
| appModel.Resources, | |
| new DistributedApplicationExecutionContext(DistributedApplicationOperation.Run), | |
| CancellationToken.None); |
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.
According to the custom XML documentation guidelines (CodingGuidelineID: 1000002), public classes require comprehensive documentation including
<remarks>. The<summary>should also be more detailed about the purpose and usage pattern.Consider expanding the documentation: