Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions playground/yarp/Yarp.AppHost/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
"ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:18100",
//"ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "https://localhost:17299",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17299",
"ASPIRE_SHOW_DASHBOARD_RESOURCES": "true",
"ASPIRE_ENABLE_CONTAINER_TUNNEL": "true"
"ASPIRE_SHOW_DASHBOARD_RESOURCES": "true"
}
},
"http": {
Expand All @@ -29,8 +28,7 @@
//"ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "http://localhost:17300",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17300",
"ASPIRE_SHOW_DASHBOARD_RESOURCES": "true",
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true",
"ASPIRE_ENABLE_CONTAINER_TUNNEL": "true"
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
}
},
"generate-manifest": {
Expand Down
2 changes: 2 additions & 0 deletions src/Aspire.Hosting.Maui/Aspire.Hosting.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
<ItemGroup>
<Compile Include="..\Shared\IConfigurationExtensions.cs" Link="Shared\IConfigurationExtensions.cs" />
<Compile Include="..\Shared\KnownConfigNames.cs" Link="Shared\KnownConfigNames.cs" />
<Compile Include="..\Shared\KnownResourceNames.cs" Link="Shared\KnownResourceNames.cs" />
<Compile Include="..\Shared\KnownEndpointNames.cs" Link="Shared\KnownEndpointNames.cs" />
<Compile Include="..\Shared\OtlpEndpointResolver.cs" Link="Shared\OtlpEndpointResolver.cs" />
<Compile Include="..\Shared\PathNormalizer.cs" Link="Shared\PathNormalizer.cs" />
<Compile Include="..\Shared\StringComparers.cs" Link="Shared\StringComparers.cs" />
Expand Down
88 changes: 70 additions & 18 deletions src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1254,50 +1254,102 @@ internal static ILogger GetLogger(this IResource resource, IServiceProvider serv
/// 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).
/// </para>
/// </remarks>
public static async Task<IReadOnlySet<IResource>> GetResourceDependenciesAsync(
public static Task<IReadOnlySet<IResource>> GetResourceDependenciesAsync(
this IResource resource,
DistributedApplicationExecutionContext executionContext,
ResourceDependencyDiscoveryMode mode = ResourceDependencyDiscoveryMode.Recursive,
CancellationToken cancellationToken = default)
{
return GetDependenciesAsync([resource], executionContext, mode, cancellationToken);
}

/// <summary>
/// Efficiently computes the set of resources that the specified source set of resources depends on.
/// </summary>
/// <param name="resources">The source set of resources to compute dependencies for.</param>
/// <param name="executionContext">The execution context for resolving environment variables and arguments.</param>
/// <param name="mode">Specifies whether to discover only direct dependencies or the full transitive closure.</param>
/// <param name="cancellationToken">A cancellation token to observe while computing dependencies.</param>
/// <returns>A set of all resources that the specified resource depends on.</returns>
/// <remarks>
/// <para>
/// Dependencies are computed from multiple sources:
/// <list type="bullet">
/// <item>Parent resources via <see cref="IResourceWithParent"/></item>
/// <item>Wait dependencies via <see cref="WaitAnnotation"/></item>
/// <item>Connection string redirects via <see cref="ConnectionStringRedirectAnnotation"/></item>
/// <item>References to endpoints in environment variables and command-line arguments (via <see cref="IValueWithReferences"/>)</item>
/// </list>
/// </para>
/// <para>
/// When <paramref name="mode"/> is <see cref="ResourceDependencyDiscoveryMode.DirectOnly"/>, only the immediate
/// dependencies are returned. When <paramref name="mode"/> is <see cref="ResourceDependencyDiscoveryMode.Recursive"/>,
/// all transitive dependencies are included.
/// </para>
/// <para>
/// This method invokes environment variable and command-line argument callbacks to discover all references.
/// </para>
/// </remarks>
internal static async Task<IReadOnlySet<IResource>> GetDependenciesAsync(
IEnumerable<IResource> resources,
DistributedApplicationExecutionContext executionContext,
ResourceDependencyDiscoveryMode mode = ResourceDependencyDiscoveryMode.Recursive,
CancellationToken cancellationToken = default)
{
var dependencies = new HashSet<IResource>();
var newDependencies = new HashSet<IResource>();
await GatherDirectDependenciesAsync(resource, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);
var toProcess = new Queue<IResource>();

if (mode == ResourceDependencyDiscoveryMode.Recursive)
foreach (var resource in resources)
{
// Compute transitive closure by recursively processing dependencies
var toProcess = new Queue<IResource>(dependencies);
while (toProcess.Count > 0)
newDependencies.Clear();
await GatherDirectDependenciesAsync(resource, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);

if (mode == ResourceDependencyDiscoveryMode.Recursive)
{
var dep = toProcess.Dequeue();
newDependencies.Clear();
// Compute transitive closure by recursively processing dependencies

await GatherDirectDependenciesAsync(dep, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);
foreach(var nd in newDependencies)
{
toProcess.Enqueue(nd);
}

foreach (var newDep in newDependencies)
while (toProcess.Count > 0)
{
if (newDep != resource)
var dep = toProcess.Dequeue();
newDependencies.Clear();

await GatherDirectDependenciesAsync(dep, dependencies, newDependencies, executionContext, cancellationToken).ConfigureAwait(false);

foreach (var newDep in newDependencies)
{
toProcess.Enqueue(newDep);
if (newDep != resource)
{
toProcess.Enqueue(newDep);
}
}
}
}
}

// Ensure the input resource is not in its own dependency set, even if referenced transitively.
dependencies.Remove(resource);
// Ensure the input resources are not in its own dependency set, even if referenced transitively.
foreach (var resource in resources)
{
dependencies.Remove(resource);
}

return dependencies;
}

/// <summary>
/// Gathers direct dependencies of a given resource.
/// </summary>
/// <returns>
/// Newly discovered dependencies (not already in <paramref name="dependencies"/>).
/// </returns>
private static async Task GatherDirectDependenciesAsync(
/// <param name="resource">The resource to gather dependencies for.</param>
/// <param name="dependencies">The set of dependencies (where dependency resources will be placed).</param>
/// <param name="newDependencies">The set of newly discovered dependencies in this invocation (not present in <paramref name="dependencies"/> at the moment of invocation).</param>
/// <param name="executionContext">The execution context for resolving environment variables and arguments.</param>
/// <param name="cancellationToken">A cancellation token to observe while gathering dependencies.</param>
internal static async Task GatherDirectDependenciesAsync(
IResource resource,
HashSet<IResource> dependencies,
HashSet<IResource> newDependencies,
Expand Down
1 change: 1 addition & 0 deletions src/Aspire.Hosting/Aspire.Hosting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Compile Include="$(SharedDir)IConfigurationExtensions.cs" Link="Utils\IConfigurationExtensions.cs" />
<Compile Include="$(SharedDir)KnownFormats.cs" Link="Utils\KnownFormats.cs" />
<Compile Include="$(SharedDir)KnownResourceNames.cs" Link="Utils\KnownResourceNames.cs" />
<Compile Include="$(SharedDir)KnownEndpointNames.cs" Link="Utils\KnownEndpointNames.cs" />
<Compile Include="$(SharedDir)EnvironmentVariableNameEncoder.cs" Link="ApplicationModel\EnvironmentVariableNameEncoder.cs" />
<Compile Include="$(SharedDir)KnownConfigNames.cs" Link="Utils\KnownConfigNames.cs" />
<Compile Include="$(SharedDir)OtlpEndpointResolver.cs" Link="Utils\OtlpEndpointResolver.cs" />
Expand Down
18 changes: 18 additions & 0 deletions src/Aspire.Hosting/AspireEventSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,4 +370,22 @@ public void StartResourceStop(string kind, string resourceName)
WriteEvent(42, kind, resourceName);
}
}

[Event(43, Level = EventLevel.Informational, Message = "DCP Service object preparation starting...")]
public void DcpServiceObjectPreparationStart()
{
if (IsEnabled())
{
WriteEvent(43);
}
}

[Event(44, Level = EventLevel.Informational, Message = "DCP Service object preparation completed")]
public void DcpServiceObjectPreparationStop()
{
if (IsEnabled())
{
WriteEvent(44);
}
}
}
10 changes: 4 additions & 6 deletions src/Aspire.Hosting/Dashboard/DashboardEventHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ IFileSystemService directoryService
) : IDistributedApplicationEventingSubscriber, IAsyncDisposable
{
// Internal for testing
internal const string OtlpGrpcEndpointName = "otlp-grpc";
internal const string OtlpHttpEndpointName = "otlp-http";
internal const string McpEndpointName = "mcp";

// Fallback defaults for framework versions and TFM
Expand Down Expand Up @@ -436,7 +434,7 @@ private void ConfigureAspireDashboardResource(IResource dashboardResource)
if (otlpGrpcEndpointUrl != null)
{
var address = BindingAddress.Parse(otlpGrpcEndpointUrl);
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: OtlpGrpcEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true, transport: "http2")
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: KnownEndpointNames.OtlpGrpcEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true, transport: "http2")
{
TargetHost = address.Host
});
Expand All @@ -445,7 +443,7 @@ private void ConfigureAspireDashboardResource(IResource dashboardResource)
if (otlpHttpEndpointUrl != null)
{
var address = BindingAddress.Parse(otlpHttpEndpointUrl);
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: OtlpHttpEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true)
dashboardResource.Annotations.Add(new EndpointAnnotation(ProtocolType.Tcp, name: KnownEndpointNames.OtlpHttpEndpointName, uriScheme: address.Scheme, port: address.Port, isProxied: true)
{
TargetHost = address.Host
});
Expand Down Expand Up @@ -660,13 +658,13 @@ private static void PopulateDashboardUrls(EnvironmentCallbackContext context)
static ReferenceExpression GetTargetUrlExpression(EndpointReference e) =>
ReferenceExpression.Create($"{e.Property(EndpointProperty.Scheme)}://{e.EndpointAnnotation.TargetHost}:{e.Property(EndpointProperty.TargetPort)}");

var otlpGrpc = dashboardResource.GetEndpoint(OtlpGrpcEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
var otlpGrpc = dashboardResource.GetEndpoint(KnownEndpointNames.OtlpGrpcEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
if (otlpGrpc.Exists)
{
context.EnvironmentVariables[DashboardConfigNames.DashboardOtlpGrpcUrlName.EnvVarName] = GetTargetUrlExpression(otlpGrpc);
}

var otlpHttp = dashboardResource.GetEndpoint(OtlpHttpEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
var otlpHttp = dashboardResource.GetEndpoint(KnownEndpointNames.OtlpHttpEndpointName, KnownNetworkIdentifiers.LocalhostNetwork);
if (otlpHttp.Exists)
{
context.EnvironmentVariables[DashboardConfigNames.DashboardOtlpHttpUrlName.EnvVarName] = GetTargetUrlExpression(otlpHttp);
Expand Down
Loading
Loading