Skip to content

Commit 8ee2469

Browse files
[release/9.1] Follow up to WaitForResourceHealthyAsync changes. (#7673)
* WIP need to change devcontainer. * Adopt Davids doc comments. * PR feedback. --------- Co-authored-by: Mitch Denny <[email protected]>
1 parent 0eaf517 commit 8ee2469

File tree

5 files changed

+40
-35
lines changed

5 files changed

+40
-35
lines changed

src/Aspire.Hosting/ApplicationModel/ResourceNotificationService.cs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public ResourceNotificationService(ILogger<ResourceNotificationService> logger,
5050
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
5151
_serviceProvider = new NullServiceProvider();
5252
_resourceLoggerService = new ResourceLoggerService();
53-
DefaultWaitBehavior = WaitBehavior.StopOnDependencyFailure;
53+
DefaultWaitBehavior = WaitBehavior.StopOnResourceUnavailable;
5454
}
5555

5656
/// <summary>
@@ -69,7 +69,7 @@ public ResourceNotificationService(
6969
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
7070
_serviceProvider = serviceProvider;
7171
_resourceLoggerService = resourceLoggerService ?? throw new ArgumentNullException(nameof(resourceLoggerService));
72-
DefaultWaitBehavior = serviceProvider.GetService<IOptions<ResourceNotificationServiceOptions>>()?.Value.DefaultWaitBehavior ?? WaitBehavior.StopOnDependencyFailure;
72+
DefaultWaitBehavior = serviceProvider.GetService<IOptions<ResourceNotificationServiceOptions>>()?.Value.DefaultWaitBehavior ?? WaitBehavior.StopOnResourceUnavailable;
7373

7474
// The IHostApplicationLifetime parameter is not used anymore, but we keep it for backwards compatibility.
7575
// Notification updates will be cancelled when the service is disposed.
@@ -161,7 +161,7 @@ async Task Core(string displayName, string resourceId)
161161
var resourceEvent = await WaitForResourceCoreAsync(dependency.Name, re => re.ResourceId == resourceId && IsContinuableState(waitBehavior, re.Snapshot), cancellationToken: cancellationToken).ConfigureAwait(false);
162162
var snapshot = resourceEvent.Snapshot;
163163

164-
if (waitBehavior == WaitBehavior.StopOnDependencyFailure)
164+
if (waitBehavior == WaitBehavior.StopOnResourceUnavailable)
165165
{
166166
if (snapshot.State?.Text == KnownResourceStates.FailedToStart)
167167
{
@@ -208,8 +208,8 @@ async Task Core(string displayName, string resourceId)
208208
static bool IsContinuableState(WaitBehavior waitBehavior, CustomResourceSnapshot snapshot) =>
209209
waitBehavior switch
210210
{
211-
WaitBehavior.WaitOnDependencyFailure => snapshot.State?.Text == KnownResourceStates.Running,
212-
WaitBehavior.StopOnDependencyFailure => snapshot.State?.Text == KnownResourceStates.Running ||
211+
WaitBehavior.WaitOnResourceUnavailable => snapshot.State?.Text == KnownResourceStates.Running,
212+
WaitBehavior.StopOnResourceUnavailable => snapshot.State?.Text == KnownResourceStates.Running ||
213213
snapshot.State?.Text == KnownResourceStates.Finished ||
214214
snapshot.State?.Text == KnownResourceStates.Exited ||
215215
snapshot.State?.Text == KnownResourceStates.FailedToStart ||
@@ -233,33 +233,25 @@ public async Task<ResourceEvent> WaitForResourceHealthyAsync(string resourceName
233233
{
234234
return await WaitForResourceHealthyAsync(
235235
resourceName,
236-
WaitBehavior.WaitOnDependencyFailure, // Retain default behavior.
236+
WaitBehavior.WaitOnResourceUnavailable, // Retain default behavior.
237237
cancellationToken).ConfigureAwait(false);
238238
}
239239

240240
/// <summary>
241241
/// Waits for a resource to become healthy.
242242
/// </summary>
243243
/// <param name="resourceName">The name of the resource.</param>
244-
/// <param name="waitBehavior">The behavior to use when waiting for the resource to become healthy.</param>
245244
/// <param name="cancellationToken">The cancellation token.</param>
245+
/// <param name="waitBehavior">The wait behavior.</param>
246246
/// <returns>A task.</returns>
247247
/// <remarks>
248248
/// This method returns a task that will complete with the resource is healthy. A resource
249-
/// without <see cref="HealthCheckAnnotation"/> annotations will be considered healthy. This overload
250-
/// will throw a <see cref="Aspire.Hosting.DistributedApplicationException"/> if the resource fails to start.
249+
/// without <see cref="HealthCheckAnnotation"/> annotations will be considered healthy.
251250
/// </remarks>
252251
public async Task<ResourceEvent> WaitForResourceHealthyAsync(string resourceName, WaitBehavior waitBehavior, CancellationToken cancellationToken = default)
253252
{
254-
var waitCondition = waitBehavior switch
255-
{
256-
WaitBehavior.WaitOnDependencyFailure => (Func<ResourceEvent, bool>)(re => re.Snapshot.HealthStatus == HealthStatus.Healthy),
257-
WaitBehavior.StopOnDependencyFailure => (Func<ResourceEvent, bool>)(re => re.Snapshot.HealthStatus == HealthStatus.Healthy || re.Snapshot.State?.Text == KnownResourceStates.FailedToStart),
258-
_ => throw new DistributedApplicationException($"Unexpected wait behavior: {waitBehavior}")
259-
};
260-
261253
_logger.LogDebug("Waiting for resource '{Name}' to enter the '{State}' state.", resourceName, HealthStatus.Healthy);
262-
var resourceEvent = await WaitForResourceCoreAsync(resourceName, waitCondition, cancellationToken: cancellationToken).ConfigureAwait(false);
254+
var resourceEvent = await WaitForResourceCoreAsync(resourceName, re => ShouldYield(waitBehavior, re.Snapshot), cancellationToken: cancellationToken).ConfigureAwait(false);
263255

264256
if (resourceEvent.Snapshot.HealthStatus != HealthStatus.Healthy)
265257
{
@@ -270,6 +262,19 @@ public async Task<ResourceEvent> WaitForResourceHealthyAsync(string resourceName
270262
_logger.LogDebug("Finished waiting for resource '{Name}'.", resourceName);
271263

272264
return resourceEvent;
265+
266+
// Determine if we should yield based on the wait behavior and the snapshot of the resource.
267+
static bool ShouldYield(WaitBehavior waitBehavior, CustomResourceSnapshot snapshot) =>
268+
waitBehavior switch
269+
{
270+
WaitBehavior.WaitOnResourceUnavailable => snapshot.HealthStatus == HealthStatus.Healthy,
271+
WaitBehavior.StopOnResourceUnavailable => snapshot.HealthStatus == HealthStatus.Healthy ||
272+
snapshot.State?.Text == KnownResourceStates.Finished ||
273+
snapshot.State?.Text == KnownResourceStates.Exited ||
274+
snapshot.State?.Text == KnownResourceStates.FailedToStart ||
275+
snapshot.State?.Text == KnownResourceStates.RuntimeUnhealthy,
276+
_ => throw new DistributedApplicationException($"Unexpected wait behavior: {waitBehavior}")
277+
};
273278
}
274279

275280
private async Task WaitUntilCompletionAsync(IResource resource, IResource dependency, int exitCode, CancellationToken cancellationToken)

src/Aspire.Hosting/ApplicationModel/WaitAnnotation.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ public enum WaitType
6060
public enum WaitBehavior
6161
{
6262
/// <summary>
63-
/// If the dependency fails, ignore the failure and continue waiting.
63+
/// If the resource is unavailable, continue waiting.
6464
/// </summary>
65-
WaitOnDependencyFailure,
65+
WaitOnResourceUnavailable,
6666

6767
/// <summary>
68-
/// If the dependency fails, stop waiting and fail the wait.
68+
/// If the resource is unavailable, stop waiting.
6969
/// </summary>
70-
StopOnDependencyFailure
70+
StopOnResourceUnavailable
7171
}

src/Aspire.Hosting/DistributedApplicationBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options)
206206
{
207207
// Default to stopping on dependency failure if the dashboard is disabled. As there's no way to see or easily recover
208208
// from a failure in that case.
209-
o.DefaultWaitBehavior = options.DisableDashboard ? WaitBehavior.StopOnDependencyFailure : WaitBehavior.WaitOnDependencyFailure;
209+
o.DefaultWaitBehavior = options.DisableDashboard ? WaitBehavior.StopOnResourceUnavailable : WaitBehavior.WaitOnResourceUnavailable;
210210
});
211211

212212
ConfigureHealthChecks();

src/Aspire.Hosting/ResourceBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ public static IResourceBuilder<T> WaitFor<T>(this IResourceBuilder<T> builder, I
738738
/// var messaging = builder.AddRabbitMQ("messaging");
739739
/// builder.AddProject&lt;Projects.MyApp&gt;("myapp")
740740
/// .WithReference(messaging)
741-
/// .WaitFor(messaging, WaitBehavior.StopOnDependencyFailure);
741+
/// .WaitFor(messaging, WaitBehavior.StopOnResourceUnavailable);
742742
/// </code>
743743
/// </example>
744744
public static IResourceBuilder<T> WaitFor<T>(this IResourceBuilder<T> builder, IResourceBuilder<IResource> dependency, WaitBehavior waitBehavior) where T : IResourceWithWaitSupport

tests/Aspire.Hosting.Tests/WaitForTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,14 @@ await app.ResourceNotifications.PublishUpdateAsync(dependency.Resource, s => s w
168168
[InlineData(nameof(KnownResourceStates.RuntimeUnhealthy))]
169169
[InlineData(nameof(KnownResourceStates.Finished))]
170170
[RequiresDocker]
171-
public async Task WaitForBehaviorStopOnDependencyFailure(string status)
171+
public async Task WaitForBehaviorStopOnResourceUnavailable(string status)
172172
{
173173
using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(testOutputHelper);
174174

175175
var dependency = builder.AddResource(new CustomResource("test"));
176176
var nginx = builder.AddContainer("nginx", "mcr.microsoft.com/cbl-mariner/base/nginx", "1.22")
177177
.WithReference(dependency)
178-
.WaitFor(dependency, WaitBehavior.StopOnDependencyFailure);
178+
.WaitFor(dependency, WaitBehavior.StopOnResourceUnavailable);
179179

180180
using var app = builder.Build();
181181

@@ -197,45 +197,45 @@ await app.ResourceNotifications.PublishUpdateAsync(dependency.Resource, s => s w
197197
}
198198

199199
[Fact]
200-
public async Task WhenWaitBehaviorIsStopOnDependencyFailureWaitForResourceHealthyAsyncShouldThrowWhenResourceFailsToStart()
200+
public async Task WhenWaitBehaviorIsStopOnResourceUnavailableWaitForResourceHealthyAsyncShouldThrowWhenResourceFailsToStart()
201201
{
202202
using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(testOutputHelper);
203203

204204
var failToStart = builder.AddExecutable("failToStart", "does-not-exist", ".");
205205
var dependency = builder.AddContainer("redis", "redis");
206206

207-
dependency.WaitFor(failToStart, WaitBehavior.StopOnDependencyFailure);
207+
dependency.WaitFor(failToStart, WaitBehavior.StopOnResourceUnavailable);
208208

209209
using var app = builder.Build();
210210
await app.StartAsync();
211211

212212
var ex = await Assert.ThrowsAsync<DistributedApplicationException>(async () => {
213213
await app.ResourceNotifications.WaitForResourceHealthyAsync(
214214
dependency.Resource.Name,
215-
WaitBehavior.StopOnDependencyFailure
215+
WaitBehavior.StopOnResourceUnavailable
216216
).WaitAsync(TimeSpan.FromSeconds(15));
217217
});
218218

219219
Assert.Equal("Stopped waiting for resource 'redis' to become healthy because it failed to start.", ex.Message);
220220
}
221221

222222
[Fact]
223-
public async Task WhenWaitBehaviorIsWaitOnDependencyFailureWaitForResourceHealthyAsyncShouldThrowWhenResourceFailsToStart()
223+
public async Task WhenWaitBehaviorIsWaitOnResourceUnavailableWaitForResourceHealthyAsyncShouldThrowWhenResourceFailsToStart()
224224
{
225225
using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(testOutputHelper);
226226

227227
var failToStart = builder.AddExecutable("failToStart", "does-not-exist", ".");
228228
var dependency = builder.AddContainer("redis", "redis");
229229

230-
dependency.WaitFor(failToStart, WaitBehavior.StopOnDependencyFailure);
230+
dependency.WaitFor(failToStart, WaitBehavior.StopOnResourceUnavailable);
231231

232232
using var app = builder.Build();
233233
await app.StartAsync();
234234

235235
var ex = await Assert.ThrowsAsync<TimeoutException>(async () => {
236236
await app.ResourceNotifications.WaitForResourceHealthyAsync(
237237
dependency.Resource.Name,
238-
WaitBehavior.WaitOnDependencyFailure
238+
WaitBehavior.WaitOnResourceUnavailable
239239
).WaitAsync(TimeSpan.FromSeconds(15));
240240
});
241241

@@ -282,14 +282,14 @@ await app.ResourceNotifications.PublishUpdateAsync(dependency.Resource, s => s w
282282
[InlineData(nameof(KnownResourceStates.RuntimeUnhealthy))]
283283
[InlineData(nameof(KnownResourceStates.Finished))]
284284
[RequiresDocker]
285-
public async Task WaitForBehaviorWaitOnDependencyFailure(string status)
285+
public async Task WaitForBehaviorWaitOnResourceUnavailable(string status)
286286
{
287287
using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(testOutputHelper);
288288

289289
var dependency = builder.AddResource(new CustomResource("test"));
290290
var nginx = builder.AddContainer("nginx", "mcr.microsoft.com/cbl-mariner/base/nginx", "1.22")
291291
.WithReference(dependency)
292-
.WaitFor(dependency, WaitBehavior.WaitOnDependencyFailure);
292+
.WaitFor(dependency, WaitBehavior.WaitOnResourceUnavailable);
293293

294294
using var app = builder.Build();
295295

@@ -324,13 +324,13 @@ await app.ResourceNotifications.PublishUpdateAsync(dependency.Resource, s => s w
324324
[InlineData(nameof(KnownResourceStates.RuntimeUnhealthy))]
325325
[InlineData(nameof(KnownResourceStates.Finished))]
326326
[RequiresDocker]
327-
public async Task WaitForBehaviorWaitOnDependencyFailureViaOptions(string status)
327+
public async Task WaitForBehaviorWaitOnResourceUnavailableViaOptions(string status)
328328
{
329329
using var builder = TestDistributedApplicationBuilder.Create().WithTestAndResourceLogging(testOutputHelper);
330330

331331
builder.Services.Configure<ResourceNotificationServiceOptions>(o =>
332332
{
333-
o.DefaultWaitBehavior = WaitBehavior.WaitOnDependencyFailure;
333+
o.DefaultWaitBehavior = WaitBehavior.WaitOnResourceUnavailable;
334334
});
335335

336336
var dependency = builder.AddResource(new CustomResource("test"));

0 commit comments

Comments
 (0)