Skip to content

Commit 4f2dcfc

Browse files
danegstaCopilotdavidfowl
authored
Remove dependencies on AfterEndpointsAllocatedEvent (#9598)
* Remove runtime usage of AfterEndpointsAllocatedEvent * Mark AfterEndpointsAllocatedEvent as obsolete to give uses warning that it will be going away * Update src/Aspire.Hosting/ApplicationModel/AfterEndpointsAllocatedEvent.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: David Fowler <davidfowl@gmail.com>
1 parent 50ead15 commit 4f2dcfc

File tree

12 files changed

+65
-76
lines changed

12 files changed

+65
-76
lines changed

src/Aspire.Hosting.Kafka/KafkaBuilderExtensions.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,19 +109,16 @@ public static IResourceBuilder<KafkaServerResource> WithKafkaUI(this IResourceBu
109109
.WithHttpEndpoint(targetPort: KafkaUIPort)
110110
.ExcludeFromManifest();
111111

112-
builder.ApplicationBuilder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((e, ct) =>
112+
builder.ApplicationBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(kafkaUi, (e, ct) =>
113113
{
114114
var kafkaResources = builder.ApplicationBuilder.Resources.OfType<KafkaServerResource>();
115115

116116
int i = 0;
117117
foreach (var kafkaResource in kafkaResources)
118118
{
119-
if (kafkaResource.InternalEndpoint.IsAllocated)
120-
{
121-
var endpoint = kafkaResource.InternalEndpoint;
122-
int index = i;
123-
kafkaUiBuilder.WithEnvironment(context => ConfigureKafkaUIContainer(context, endpoint, index));
124-
}
119+
var endpoint = kafkaResource.InternalEndpoint;
120+
int index = i;
121+
kafkaUiBuilder.WithEnvironment(context => ConfigureKafkaUIContainer(context, endpoint, index));
125122

126123
i++;
127124
}

src/Aspire.Hosting.MySql/MySqlBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public static IResourceBuilder<T> WithPhpMyAdmin<T>(this IResourceBuilder<T> bui
205205
.WithHttpEndpoint(targetPort: 80, name: "http")
206206
.ExcludeFromManifest();
207207

208-
builder.ApplicationBuilder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((e, ct) =>
208+
builder.ApplicationBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(phpMyAdminContainer, (e, ct) =>
209209
{
210210
var mySqlInstances = builder.ApplicationBuilder.Resources.OfType<MySqlServerResource>();
211211

src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public static IResourceBuilder<RedisResource> AddRedis(
6464

6565
// StackExchange.Redis doesn't support passwords with commas.
6666
// See https://github.com/StackExchange/StackExchange.Redis/issues/680 and
67-
// https://github.com/Azure/azure-dev/issues/4848
67+
// https://github.com/Azure/azure-dev/issues/4848
6868
var passwordParameter = password?.Resource ?? ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $"{name}-password", special: false);
6969

7070
var redis = new RedisResource(name, passwordParameter);
@@ -158,7 +158,7 @@ public static IResourceBuilder<RedisResource> WithRedisCommander(this IResourceB
158158
.WithHttpEndpoint(targetPort: 8081, name: "http")
159159
.ExcludeFromManifest();
160160

161-
builder.ApplicationBuilder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((e, ct) =>
161+
builder.ApplicationBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(resource, (e, ct) =>
162162
{
163163
var redisInstances = builder.ApplicationBuilder.Resources.OfType<RedisResource>();
164164

@@ -172,17 +172,14 @@ public static IResourceBuilder<RedisResource> WithRedisCommander(this IResourceB
172172

173173
foreach (var redisInstance in redisInstances)
174174
{
175-
if (redisInstance.PrimaryEndpoint.IsAllocated)
175+
// Redis Commander assumes Redis is being accessed over a default Aspire container network and hardcodes the resource address
176+
// This will need to be refactored once updated service discovery APIs are available
177+
var hostString = $"{(hostsVariableBuilder.Length > 0 ? "," : string.Empty)}{redisInstance.Name}:{redisInstance.Name}:{redisInstance.PrimaryEndpoint.TargetPort}:0";
178+
if (redisInstance.PasswordParameter is not null)
176179
{
177-
// Redis Commander assumes Redis is being accessed over a default Aspire container network and hardcodes the resource address
178-
// This will need to be refactored once updated service discovery APIs are available
179-
var hostString = $"{(hostsVariableBuilder.Length > 0 ? "," : string.Empty)}{redisInstance.Name}:{redisInstance.Name}:{redisInstance.PrimaryEndpoint.TargetPort}:0";
180-
if (redisInstance.PasswordParameter is not null)
181-
{
182-
hostString += $":{redisInstance.PasswordParameter.Value}";
183-
}
184-
hostsVariableBuilder.Append(hostString);
180+
hostString += $":{redisInstance.PasswordParameter.Value}";
185181
}
182+
hostsVariableBuilder.Append(hostString);
186183
}
187184

188185
resourceBuilder.WithEnvironment("REDIS_HOSTS", hostsVariableBuilder.ToString());

src/Aspire.Hosting/ApplicationModel/AfterEndpointsAllocatedEvent.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace Aspire.Hosting.ApplicationModel;
2626
/// </code>
2727
/// </example>
2828
/// </remarks>
29+
[Obsolete("The AfterEndpointsAllocatedEvent is deprecated and will be removed in a future version. Use the resource specific events BeforeResourceStartedEvent or ResourceEndpointsAllocatedEvent instead depending on your needs.")]
2930
public class AfterEndpointsAllocatedEvent(IServiceProvider services, DistributedApplicationModel model) : IDistributedApplicationEvent
3031
{
3132
/// <summary>

src/Aspire.Hosting/Orchestrator/ApplicationOrchestrator.cs

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public ApplicationOrchestrator(DistributedApplicationModel model,
5252
dcpExecutorEvents.Subscribe<OnResourceStartingContext>(OnResourceStarting);
5353
dcpExecutorEvents.Subscribe<OnResourceFailedToStartContext>(OnResourceFailedToStart);
5454

55-
_eventing.Subscribe<AfterEndpointsAllocatedEvent>(ProcessResourcesWithoutLifetime);
55+
_eventing.Subscribe<ResourceEndpointsAllocatedEvent>(ProcessResourcesWithoutLifetime);
5656
_eventing.Subscribe<ResourceEndpointsAllocatedEvent>(PublishInitialResourceUrls);
5757
// Implement WaitFor functionality using BeforeResourceStartedEvent.
5858
_eventing.Subscribe<BeforeResourceStartedEvent>(WaitForInBeforeResourceStartedEvent);
@@ -94,7 +94,9 @@ private async Task WaitForInBeforeResourceStartedEvent(BeforeResourceStartedEven
9494

9595
private async Task OnEndpointsAllocated(OnEndpointsAllocatedContext context)
9696
{
97+
#pragma warning disable CS0618 // Type or member is obsolete
9798
var afterEndpointsAllocatedEvent = new AfterEndpointsAllocatedEvent(_serviceProvider, _model);
99+
#pragma warning restore CS0618 // Type or member is obsolete
98100
await _eventing.PublishAsync(afterEndpointsAllocatedEvent, context.CancellationToken).ConfigureAwait(false);
99101

100102
foreach (var lifecycleHook in _lifecycleHooks)
@@ -240,48 +242,45 @@ private async Task ProcessUrls(IResource resource, CancellationToken cancellatio
240242
}
241243
}
242244

243-
private Task ProcessResourcesWithoutLifetime(AfterEndpointsAllocatedEvent @event, CancellationToken cancellationToken)
245+
private async Task ProcessResourcesWithoutLifetime(ResourceEndpointsAllocatedEvent @event, CancellationToken cancellationToken)
244246
{
245-
async Task ProcessValueAsync(IResource resource, IValueProvider vp)
247+
if (@event.Resource is not IResourceWithoutLifetime resource)
246248
{
247-
try
248-
{
249-
var value = await vp.GetValueAsync(default).ConfigureAwait(false);
250-
251-
await _notificationService.PublishUpdateAsync(resource, s =>
252-
{
253-
return s with
254-
{
255-
Properties = s.Properties.SetResourceProperty("Value", value ?? "", resource is ParameterResource p && p.Secret)
256-
};
257-
})
258-
.ConfigureAwait(false);
259-
}
260-
catch (Exception ex)
261-
{
262-
await _notificationService.PublishUpdateAsync(resource, s =>
263-
{
264-
return s with
265-
{
266-
State = new("Value missing", KnownResourceStateStyles.Error),
267-
Properties = s.Properties.SetResourceProperty("Value", ex.Message)
268-
};
269-
})
270-
.ConfigureAwait(false);
249+
return;
250+
}
271251

272-
_loggerService.GetLogger(resource.Name).LogError("{Message}", ex.Message);
273-
}
252+
if (resource is not IValueProvider valueProvider)
253+
{
254+
return;
274255
}
275256

276-
foreach (var resource in _model.Resources.OfType<IResourceWithoutLifetime>())
257+
try
277258
{
278-
if (resource is IValueProvider provider)
259+
var value = await valueProvider.GetValueAsync(default).ConfigureAwait(false);
260+
261+
await _notificationService.PublishUpdateAsync(resource, s =>
279262
{
280-
_ = ProcessValueAsync(resource, provider);
281-
}
263+
return s with
264+
{
265+
Properties = s.Properties.SetResourceProperty("Value", value ?? "", resource is ParameterResource p && p.Secret)
266+
};
267+
})
268+
.ConfigureAwait(false);
282269
}
270+
catch (Exception ex)
271+
{
272+
await _notificationService.PublishUpdateAsync(resource, s =>
273+
{
274+
return s with
275+
{
276+
State = new("Value missing", KnownResourceStateStyles.Error),
277+
Properties = s.Properties.SetResourceProperty("Value", ex.Message)
278+
};
279+
})
280+
.ConfigureAwait(false);
283281

284-
return Task.CompletedTask;
282+
_loggerService.GetLogger(resource.Name).LogError("{Message}", ex.Message);
283+
}
285284
}
286285

287286
private async Task OnResourceChanged(OnResourceChangedContext context)

src/Aspire.Hosting/ResourceBuilderExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1312,7 +1312,7 @@ public static IResourceBuilder<T> WithHttpHealthCheck<T>(this IResourceBuilder<T
13121312

13131313
var endpointName = endpoint.EndpointName;
13141314

1315-
builder.ApplicationBuilder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((@event, ct) =>
1315+
builder.ApplicationBuilder.Eventing.Subscribe<ResourceEndpointsAllocatedEvent>(builder.Resource, (@event, ct) =>
13161316
{
13171317
if (!endpoint.Exists)
13181318
{

tests/Aspire.Hosting.MySql.Tests/AddMySqlTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,10 @@ public async Task SingleMySqlInstanceProducesCorrectMySqlHostsVariable()
239239
// Add fake allocated endpoints.
240240
mysql.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5001));
241241

242-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
243-
244242
var myAdmin = builder.Resources.Single(r => r.Name.Equals("phpmyadmin"));
245243

244+
await builder.Eventing.PublishAsync<BeforeResourceStartedEvent>(new(myAdmin, app.Services));
245+
246246
var config = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(myAdmin, DistributedApplicationOperation.Run, TestServiceProvider.Instance);
247247

248248
var container = builder.Resources.Single(r => r.Name == "phpmyadmin");
@@ -271,9 +271,9 @@ public void WithPhpMyAdminProducesValidServerConfigFile()
271271
using var app = builder.Build();
272272
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
273273

274-
builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
275-
276274
var myAdmin = builder.Resources.Single(r => r.Name.Equals("phpmyadmin"));
275+
builder.Eventing.PublishAsync<BeforeResourceStartedEvent>(new(myAdmin, app.Services));
276+
277277
var volume = myAdmin.Annotations.OfType<ContainerMountAnnotation>().Single();
278278

279279
using var stream = File.OpenRead(volume.Source!);

tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -369,17 +369,14 @@ public async Task VerifyManifestWithParameters()
369369
}
370370

371371
[Fact]
372-
public async Task WithPgAdminAddsContainer()
372+
public void WithPgAdminAddsContainer()
373373
{
374374
using var builder = TestDistributedApplicationBuilder.Create();
375375
builder.AddPostgres("mypostgres").WithPgAdmin(pga => pga.WithHostPort(8081));
376376

377377
using var app = builder.Build();
378378
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
379379

380-
// The mount annotation is added in the AfterEndpointsAllocatedEvent.
381-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
382-
383380
var container = builder.Resources.Single(r => r.Name == "pgadmin");
384381
var createFile = container.Annotations.OfType<ContainerFileSystemCallbackAnnotation>().Single();
385382

@@ -468,8 +465,6 @@ public async Task WithPostgresProducesValidServersJsonFile()
468465

469466
using var app = builder.Build();
470467

471-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
472-
473468
var pgadmin = builder.Resources.Single(r => r.Name.Equals("pgadmin"));
474469

475470
var createServers = pgadmin.Annotations.OfType<ContainerFileSystemCallbackAnnotation>().Single();
@@ -531,8 +526,6 @@ public async Task WithPgwebProducesValidBookmarkFiles()
531526
using var app = builder.Build();
532527
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
533528

534-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
535-
536529
var pgweb = builder.Resources.Single(r => r.Name.Equals("pgweb"));
537530
var createBookmarks = pgweb.Annotations.OfType<ContainerFileSystemCallbackAnnotation>().Single();
538531

tests/Aspire.Hosting.Qdrant.Tests/QdrantFunctionalTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,10 @@ public async Task AddQdrantWithDefaultsAddsUrlAnnotations()
227227

228228
var qdrant = builder.AddQdrant("qdrant");
229229

230+
var qdrantResource = builder.Resources.Single(r => r.Name.Equals("qdrant"));
231+
230232
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
231-
builder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((e, ct) =>
233+
builder.Eventing.Subscribe<ConnectionStringAvailableEvent>(qdrantResource, (e, ct) =>
232234
{
233235
tcs.SetResult();
234236
return Task.CompletedTask;

tests/Aspire.Hosting.Redis.Tests/AddRedisTests.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,6 @@ public async Task WithRedisInsightProducesCorrectEnvironmentVariables()
300300
redis1.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5001));
301301
redis2.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5002));
302302

303-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
304-
305303
var redisInsight = Assert.Single(builder.Resources.OfType<RedisInsightResource>());
306304
var envs = await redisInsight.GetEnvironmentVariableValuesAsync();
307305

@@ -490,10 +488,10 @@ public async Task SingleRedisInstanceWithoutPasswordProducesCorrectRedisHostsVar
490488
// Add fake allocated endpoints.
491489
redis.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5001));
492490

493-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
494-
495491
var commander = builder.Resources.Single(r => r.Name.Equals("rediscommander"));
496492

493+
await builder.Eventing.PublishAsync<BeforeResourceStartedEvent>(new(commander, app.Services));
494+
497495
var config = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(
498496
commander,
499497
DistributedApplicationOperation.Run,
@@ -514,10 +512,10 @@ public async Task SingleRedisInstanceWithPasswordProducesCorrectRedisHostsVariab
514512
// Add fake allocated endpoints.
515513
redis.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5001));
516514

517-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
518-
519515
var commander = builder.Resources.Single(r => r.Name.Equals("rediscommander"));
520516

517+
await builder.Eventing.PublishAsync<BeforeResourceStartedEvent>(new(commander, app.Services));
518+
521519
var config = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(commander);
522520

523521
Assert.Equal($"myredis1:{redis.Resource.Name}:6379:0:{password}", config["REDIS_HOSTS"]);
@@ -535,10 +533,10 @@ public async Task MultipleRedisInstanceProducesCorrectRedisHostsVariable()
535533
redis1.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5001));
536534
redis2.WithEndpoint("tcp", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", 5002, "host2"));
537535

538-
await builder.Eventing.PublishAsync<AfterEndpointsAllocatedEvent>(new(app.Services, app.Services.GetRequiredService<DistributedApplicationModel>()));
539-
540536
var commander = builder.Resources.Single(r => r.Name.Equals("rediscommander"));
541537

538+
await builder.Eventing.PublishAsync<BeforeResourceStartedEvent>(new(commander, app.Services));
539+
542540
var config = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(
543541
commander,
544542
DistributedApplicationOperation.Run,

tests/Aspire.Hosting.Tests/Eventing/DistributedApplicationBuilderEventingTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,15 @@ public async Task LifeycleHookAnalogousEventsFire()
238238
beforeStartEventFired.Set();
239239
return Task.CompletedTask;
240240
});
241+
#pragma warning disable CS0618 // Type or member is obsolete
241242
builder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((e, ct) =>
242243
{
243244
Assert.NotNull(e.Services);
244245
Assert.NotNull(e.Model);
245246
afterEndpointsAllocatedEventFired.Set();
246247
return Task.CompletedTask;
247248
});
249+
#pragma warning restore CS0618 // Type or member is obsolete
248250
builder.Eventing.Subscribe<AfterResourcesCreatedEvent>((e, ct) =>
249251
{
250252
Assert.NotNull(e.Services);

tests/Aspire.Hosting.Tests/WithUrlsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public async Task WithUrlsCallsCallbackAfterBeforeResourceStartedEvent()
5353
.WithUrls(c => called = true);
5454

5555
var tcs = new TaskCompletionSource();
56-
builder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((e, ct) =>
56+
builder.Eventing.Subscribe<ResourceEndpointsAllocatedEvent>(projectA.Resource, (e, ct) =>
5757
{
5858
// Should not be called at this point
5959
Assert.False(called);

0 commit comments

Comments
 (0)