Skip to content

Commit 0b5acf0

Browse files
authored
Enable container tunnel by default (#14557)
* Dependencies of multiple resources * DCP object creation as set of tasks ... to distinguish between "regular" and "tunnel" services * Enable tunnel by default * Clean up startup performance script * Bug fixes * Fix Kafka tests * Fix WaitFor test for failing resource * Remove unnecessary references from Hosting.Maui project * Feedback from Eric
1 parent 3adee5f commit 0b5acf0

24 files changed

+907
-151
lines changed

playground/yarp/Yarp.AppHost/Properties/launchSettings.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
"ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:18100",
1313
//"ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "https://localhost:17299",
1414
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17299",
15-
"ASPIRE_SHOW_DASHBOARD_RESOURCES": "true",
16-
"ASPIRE_ENABLE_CONTAINER_TUNNEL": "true"
15+
"ASPIRE_SHOW_DASHBOARD_RESOURCES": "true"
1716
}
1817
},
1918
"http": {
@@ -29,8 +28,7 @@
2928
//"ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "http://localhost:17300",
3029
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17300",
3130
"ASPIRE_SHOW_DASHBOARD_RESOURCES": "true",
32-
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true",
33-
"ASPIRE_ENABLE_CONTAINER_TUNNEL": "true"
31+
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
3432
}
3533
},
3634
"generate-manifest": {

src/Aspire.Hosting.Kafka/KafkaBuilderExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Globalization;
54
using Aspire.Hosting.ApplicationModel;
65
using Confluent.Kafka;
76
using HealthChecks.Kafka;
@@ -215,7 +214,7 @@ private static void ConfigureKafkaContainer(EnvironmentCallbackContext context,
215214
var advertisedListeners = context.ExecutionContext.IsRunMode
216215
// In run mode, PLAINTEXT_INTERNAL assumes kafka is being accessed over a default Aspire container network and hardcodes the resource address
217216
// This will need to be refactored once updated service discovery APIs are available
218-
? ReferenceExpression.Create($"PLAINTEXT://localhost:29092,PLAINTEXT_HOST://localhost:{primaryEndpoint.Port.ToString(CultureInfo.InvariantCulture)},PLAINTEXT_INTERNAL://{resource.Name}:{internalEndpoint.Property(EndpointProperty.TargetPort)}")
217+
? ReferenceExpression.Create($"PLAINTEXT://localhost:29092,PLAINTEXT_HOST://localhost:{primaryEndpoint.Property(EndpointProperty.Port)},PLAINTEXT_INTERNAL://{resource.Name}:{internalEndpoint.Property(EndpointProperty.TargetPort)}")
219218
: ReferenceExpression.Create($"PLAINTEXT://{primaryEndpoint.Property(EndpointProperty.Host)}:29092,PLAINTEXT_HOST://{primaryEndpoint.Property(EndpointProperty.HostAndPort)},PLAINTEXT_INTERNAL://{internalEndpoint.Property(EndpointProperty.HostAndPort)}");
220219

221220
context.EnvironmentVariables["KAFKA_ADVERTISED_LISTENERS"] = advertisedListeners;

src/Aspire.Hosting.Kafka/KafkaServerResource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class KafkaServerResource(string name) : ContainerResource(name), IResour
2323
/// Gets the primary endpoint for the Kafka broker. This endpoint is used for host processes to Kafka broker communication.
2424
/// To connect to the Kafka broker from a container, use <see cref="InternalEndpoint"/>.
2525
/// </summary>
26-
public EndpointReference PrimaryEndpoint => _primaryEndpoint ??= new(this, PrimaryEndpointName);
26+
public EndpointReference PrimaryEndpoint => _primaryEndpoint ??= new(this, PrimaryEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
2727

2828
/// <summary>
2929
/// Gets the host endpoint reference for the primary endpoint.
@@ -39,7 +39,7 @@ public class KafkaServerResource(string name) : ContainerResource(name), IResour
3939
/// Gets the internal endpoint for the Kafka broker. This endpoint is used for container to broker communication.
4040
/// To connect to the Kafka broker from a host process, use <see cref="PrimaryEndpoint"/>.
4141
/// </summary>
42-
public EndpointReference InternalEndpoint => _internalEndpoint ??= new(this, InternalEndpointName);
42+
public EndpointReference InternalEndpoint => _internalEndpoint ??= new(this, InternalEndpointName, KnownNetworkIdentifiers.DefaultAspireContainerNetwork);
4343

4444
/// <summary>
4545
/// Gets the connection string expression for the Kafka broker.

src/Aspire.Hosting/ApplicationModel/HostUrl.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,13 @@ public record HostUrl(string Url) : IValueProvider, IManifestExpressionProvider
5555
// Determine what hostname means that we want to contact the host machine from the container. If using the new tunnel feature, this needs to be the address of the tunnel instance.
5656
// Otherwise we want to try and determine the container runtime appropriate hostname (host.docker.internal or host.containers.internal).
5757
uri.Host = options.Value.EnableAspireContainerTunnel? KnownHostNames.DefaultContainerTunnelHostName : dcpInfo?.Containers?.ContainerHostName ?? KnownHostNames.DockerDesktopHostBridge;
58+
var model = context.ExecutionContext.ServiceProvider.GetService<DistributedApplicationModel>();
5859

59-
if (options.Value.EnableAspireContainerTunnel)
60+
if (options.Value.EnableAspireContainerTunnel && model is { })
6061
{
6162
// If we're running with the container tunnel enabled, we need to lookup the port on the tunnel that corresponds to the
6263
// target port on the host machine.
63-
var model = context.ExecutionContext.ServiceProvider.GetRequiredService<DistributedApplicationModel>();
64+
6465
var targetEndpoint = model.Resources.Where(r => !r.IsContainer())
6566
.OfType<IResourceWithEndpoints>()
6667
.Select(r =>

src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,49 +1254,101 @@ internal static ILogger GetLogger(this IResource resource, IServiceProvider serv
12541254
/// This method invokes environment variable and command-line argument callbacks to discover all references. The context resource (<paramref name="resource"/>) is not considered a dependency (even if it is transitively referenced).
12551255
/// </para>
12561256
/// </remarks>
1257-
public static async Task<IReadOnlySet<IResource>> GetResourceDependenciesAsync(
1257+
public static Task<IReadOnlySet<IResource>> GetResourceDependenciesAsync(
12581258
this IResource resource,
12591259
DistributedApplicationExecutionContext executionContext,
12601260
ResourceDependencyDiscoveryMode mode = ResourceDependencyDiscoveryMode.Recursive,
12611261
CancellationToken cancellationToken = default)
1262+
{
1263+
return GetDependenciesAsync([resource], executionContext, mode, cancellationToken);
1264+
}
1265+
1266+
/// <summary>
1267+
/// Efficiently computes the set of resources that the specified source set of resources depends on.
1268+
/// </summary>
1269+
/// <param name="resources">The source set of resources to compute dependencies for.</param>
1270+
/// <param name="executionContext">The execution context for resolving environment variables and arguments.</param>
1271+
/// <param name="mode">Specifies whether to discover only direct dependencies or the full transitive closure.</param>
1272+
/// <param name="cancellationToken">A cancellation token to observe while computing dependencies.</param>
1273+
/// <returns>A set of all resources that the specified resource depends on.</returns>
1274+
/// <remarks>
1275+
/// <para>
1276+
/// Dependencies are computed from multiple sources:
1277+
/// <list type="bullet">
1278+
/// <item>Parent resources via <see cref="IResourceWithParent"/></item>
1279+
/// <item>Wait dependencies via <see cref="WaitAnnotation"/></item>
1280+
/// <item>Connection string redirects via <see cref="ConnectionStringRedirectAnnotation"/></item>
1281+
/// <item>References to endpoints in environment variables and command-line arguments (via <see cref="IValueWithReferences"/>)</item>
1282+
/// </list>
1283+
/// </para>
1284+
/// <para>
1285+
/// When <paramref name="mode"/> is <see cref="ResourceDependencyDiscoveryMode.DirectOnly"/>, only the immediate
1286+
/// dependencies are returned. When <paramref name="mode"/> is <see cref="ResourceDependencyDiscoveryMode.Recursive"/>,
1287+
/// all transitive dependencies are included.
1288+
/// </para>
1289+
/// <para>
1290+
/// This method invokes environment variable and command-line argument callbacks to discover all references.
1291+
/// </para>
1292+
/// </remarks>
1293+
internal static async Task<IReadOnlySet<IResource>> GetDependenciesAsync(
1294+
IEnumerable<IResource> resources,
1295+
DistributedApplicationExecutionContext executionContext,
1296+
ResourceDependencyDiscoveryMode mode = ResourceDependencyDiscoveryMode.Recursive,
1297+
CancellationToken cancellationToken = default)
12621298
{
12631299
var dependencies = new HashSet<IResource>();
12641300
var newDependencies = new HashSet<IResource>();
1265-
await GatherDirectDependenciesAsync(resource, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);
1301+
var toProcess = new Queue<IResource>();
12661302

1267-
if (mode == ResourceDependencyDiscoveryMode.Recursive)
1303+
foreach (var resource in resources)
12681304
{
1269-
// Compute transitive closure by recursively processing dependencies
1270-
var toProcess = new Queue<IResource>(dependencies);
1271-
while (toProcess.Count > 0)
1305+
newDependencies.Clear();
1306+
await GatherDirectDependenciesAsync(resource, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);
1307+
1308+
if (mode == ResourceDependencyDiscoveryMode.Recursive)
12721309
{
1273-
var dep = toProcess.Dequeue();
1274-
newDependencies.Clear();
1310+
// Compute transitive closure by recursively processing dependencies
12751311

1276-
await GatherDirectDependenciesAsync(dep, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);
1312+
foreach(var nd in newDependencies)
1313+
{
1314+
toProcess.Enqueue(nd);
1315+
}
12771316

1278-
foreach (var newDep in newDependencies)
1317+
while (toProcess.Count > 0)
12791318
{
1280-
if (newDep != resource)
1319+
var dep = toProcess.Dequeue();
1320+
newDependencies.Clear();
1321+
1322+
await GatherDirectDependenciesAsync(dep, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);
1323+
1324+
foreach (var newDep in newDependencies)
12811325
{
1282-
toProcess.Enqueue(newDep);
1326+
if (newDep != resource)
1327+
{
1328+
toProcess.Enqueue(newDep);
1329+
}
12831330
}
12841331
}
12851332
}
12861333
}
12871334

1288-
// Ensure the input resource is not in its own dependency set, even if referenced transitively.
1289-
dependencies.Remove(resource);
1335+
// Ensure the input resources are not in its own dependency set, even if referenced transitively.
1336+
foreach (var resource in resources)
1337+
{
1338+
dependencies.Remove(resource);
1339+
}
12901340

12911341
return dependencies;
12921342
}
12931343

12941344
/// <summary>
12951345
/// Gathers direct dependencies of a given resource.
12961346
/// </summary>
1297-
/// <returns>
1298-
/// Newly discovered dependencies (not already in <paramref name="dependencies"/>).
1299-
/// </returns>
1347+
/// <param name="resource">The resource to gather dependencies for.</param>
1348+
/// <param name="dependencies">The set of dependencies (where dependency resources will be placed).</param>
1349+
/// <param name="newDependencies">The set of newly discovered dependencies in this invocation (not present in <paramref name="dependencies"/> at the moment of invocation).</param>
1350+
/// <param name="executionContext">The execution context for resolving environment variables and arguments.</param>
1351+
/// <param name="cancellationToken">A cancellation token to observe while gathering dependencies.</param>
13001352
private static async Task GatherDirectDependenciesAsync(
13011353
IResource resource,
13021354
HashSet<IResource> dependencies,

src/Aspire.Hosting/Aspire.Hosting.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<Compile Include="$(SharedDir)IConfigurationExtensions.cs" Link="Utils\IConfigurationExtensions.cs" />
2323
<Compile Include="$(SharedDir)KnownFormats.cs" Link="Utils\KnownFormats.cs" />
2424
<Compile Include="$(SharedDir)KnownResourceNames.cs" Link="Utils\KnownResourceNames.cs" />
25+
<Compile Include="$(SharedDir)KnownEndpointNames.cs" Link="Utils\KnownEndpointNames.cs" />
2526
<Compile Include="$(SharedDir)EnvironmentVariableNameEncoder.cs" Link="ApplicationModel\EnvironmentVariableNameEncoder.cs" />
2627
<Compile Include="$(SharedDir)KnownConfigNames.cs" Link="Utils\KnownConfigNames.cs" />
2728
<Compile Include="$(SharedDir)OtlpEndpointResolver.cs" Link="Utils\OtlpEndpointResolver.cs" />

src/Aspire.Hosting/AspireEventSource.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,4 +370,22 @@ public void StartResourceStop(string kind, string resourceName)
370370
WriteEvent(42, kind, resourceName);
371371
}
372372
}
373+
374+
[Event(43, Level = EventLevel.Informational, Message = "DCP Service object preparation starting...")]
375+
public void DcpServiceObjectPreparationStart()
376+
{
377+
if (IsEnabled())
378+
{
379+
WriteEvent(43);
380+
}
381+
}
382+
383+
[Event(44, Level = EventLevel.Informational, Message = "DCP Service object preparation completed")]
384+
public void DcpServiceObjectPreparationStop()
385+
{
386+
if (IsEnabled())
387+
{
388+
WriteEvent(44);
389+
}
390+
}
373391
}

src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ IFileSystemService directoryService
4444
) : IDistributedApplicationEventingSubscriber, IAsyncDisposable
4545
{
4646
// Internal for testing
47-
internal const string OtlpGrpcEndpointName = "otlp-grpc";
48-
internal const string OtlpHttpEndpointName = "otlp-http";
4947
internal const string McpEndpointName = "mcp";
5048

5149
// Fallback defaults for framework versions and TFM
@@ -436,7 +434,7 @@ private void ConfigureAspireDashboardResource(IResource dashboardResource)
436434
if (otlpGrpcEndpointUrl != null)
437435
{
438436
var address = BindingAddress.Parse(otlpGrpcEndpointUrl);
439-
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: OtlpGrpcEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true, transport: "http2")
437+
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: KnownEndpointNames.OtlpGrpcEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true, transport: "http2")
440438
{
441439
TargetHost = address.Host
442440
});
@@ -445,7 +443,7 @@ private void ConfigureAspireDashboardResource(IResource dashboardResource)
445443
if (otlpHttpEndpointUrl != null)
446444
{
447445
var address = BindingAddress.Parse(otlpHttpEndpointUrl);
448-
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: OtlpHttpEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true)
446+
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: KnownEndpointNames.OtlpHttpEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true)
449447
{
450448
TargetHost = address.Host
451449
});
@@ -660,13 +658,13 @@ private static void PopulateDashboardUrls(EnvironmentCallbackContext context)
660658
static ReferenceExpression GetTargetUrlExpression(EndpointReference e) =>
661659
ReferenceExpression.Create($"{e.Property(EndpointProperty.Scheme)}://{e.EndpointAnnotation.TargetHost}:{e.Property(EndpointProperty.TargetPort)}");
662660

663-
var otlpGrpc = dashboardResource.GetEndpoint(OtlpGrpcEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
661+
var otlpGrpc = dashboardResource.GetEndpoint(KnownEndpointNames.OtlpGrpcEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
664662
if (otlpGrpc.Exists)
665663
{
666664
context.EnvironmentVariables[DashboardConfigNames.DashboardOtlpGrpcUrlName.EnvVarName] = GetTargetUrlExpression(otlpGrpc);
667665
}
668666

669-
var otlpHttp = dashboardResource.GetEndpoint(OtlpHttpEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
667+
var otlpHttp = dashboardResource.GetEndpoint(KnownEndpointNames.OtlpHttpEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
670668
if (otlpHttp.Exists)
671669
{
672670
context.EnvironmentVariables[DashboardConfigNames.DashboardOtlpHttpUrlName.EnvVarName] = GetTargetUrlExpression(otlpHttp);

0 commit comments

Comments
 (0)