Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions playground/yarp/Yarp.AppHost/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"DcpPublisher": {
"EnableAspireContainerTunnel": true
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,25 @@ public static Uri GetEndpoint(this DistributedApplication app, string resourceNa
ArgumentNullException.ThrowIfNull(app);
ArgumentException.ThrowIfNullOrEmpty(resourceName);

return new(GetEndpointUriStringCore(app, resourceName, endpointName));
return GetEndpointForNetwork(app, resourceName, null, endpointName);
}

/// <summary>
/// Gets the endpoint for the specified resource.
/// </summary>
/// <param name="app">The application.</param>
/// <param name="resourceName">The resource name.</param>
/// <param name="networkIdentifier">The optional network identifier. If none is specified, the default network is used.</param>
/// <param name="endpointName">The optional endpoint name. If none are specified, the single defined endpoint is returned.</param>
/// <returns>A URI representation of the endpoint.</returns>
/// <exception cref="ArgumentException">The resource was not found, no matching endpoint was found, or multiple endpoints were found.</exception>
/// <exception cref="InvalidOperationException">The resource has no endpoints.</exception>
public static Uri GetEndpointForNetwork(this DistributedApplication app, string resourceName, NetworkIdentifier? networkIdentifier, string? endpointName = default)
{
ArgumentNullException.ThrowIfNull(app);
ArgumentException.ThrowIfNullOrEmpty(resourceName);

return new(GetEndpointUriStringCore(app, resourceName, endpointName, networkIdentifier));
}

static IResource GetResource(DistributedApplication app, string resourceName)
Expand All @@ -87,7 +105,7 @@ static IResource GetResource(DistributedApplication app, string resourceName)
return resource;
}

static string GetEndpointUriStringCore(DistributedApplication app, string resourceName, string? endpointName = default)
static string GetEndpointUriStringCore(DistributedApplication app, string resourceName, string? endpointName = default, NetworkIdentifier? networkIdentifier = default)
{
var resource = GetResource(app, resourceName);
if (resource is not IResourceWithEndpoints resourceWithEndpoints)
Expand All @@ -98,11 +116,11 @@ static string GetEndpointUriStringCore(DistributedApplication app, string resour
EndpointReference? endpoint;
if (!string.IsNullOrEmpty(endpointName))
{
endpoint = GetEndpointOrDefault(resourceWithEndpoints, endpointName);
endpoint = GetEndpointOrDefault(resourceWithEndpoints, endpointName, networkIdentifier);
}
else
{
endpoint = GetEndpointOrDefault(resourceWithEndpoints, "http") ?? GetEndpointOrDefault(resourceWithEndpoints, "https");
endpoint = GetEndpointOrDefault(resourceWithEndpoints, "http", networkIdentifier) ?? GetEndpointOrDefault(resourceWithEndpoints, "https", networkIdentifier);
}

if (endpoint is null)
Expand All @@ -122,9 +140,9 @@ static void ThrowIfNotStarted(DistributedApplication app)
}
}

static EndpointReference? GetEndpointOrDefault(IResourceWithEndpoints resourceWithEndpoints, string endpointName)
static EndpointReference? GetEndpointOrDefault(IResourceWithEndpoints resourceWithEndpoints, string endpointName, NetworkIdentifier? networkIdentifier = default)
{
var reference = resourceWithEndpoints.GetEndpoint(endpointName);
var reference = resourceWithEndpoints.GetEndpoint(endpointName, networkIdentifier ?? KnownNetworkIdentifiers.LocalhostNetwork);

return reference.IsAllocated ? reference : null;
}
Expand Down
16 changes: 3 additions & 13 deletions src/Aspire.Hosting/ApplicationModel/ConnectionStringReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,12 @@ public class ConnectionStringReference(IResourceWithConnectionString resource, b

ValueTask<string?> IValueProvider.GetValueAsync(CancellationToken cancellationToken)
{
return this.GetNetworkValueAsync(null, cancellationToken);
return Resource.GetValueAsync(cancellationToken);
}

ValueTask<string?> IValueProvider.GetValueAsync(ValueProviderContext context, CancellationToken cancellationToken)
async ValueTask<string?> IValueProvider.GetValueAsync(ValueProviderContext context, CancellationToken cancellationToken)
{
return context.Network switch
{
NetworkIdentifier networkContext => GetNetworkValueAsync(networkContext, cancellationToken),
_ => GetNetworkValueAsync(null, cancellationToken)
};
}

private async ValueTask<string?> GetNetworkValueAsync(NetworkIdentifier? networkContext, CancellationToken cancellationToken)
{
ValueProviderContext vpc = new() { Network = networkContext };
var value = await Resource.GetValueAsync(vpc, cancellationToken).ConfigureAwait(false);
var value = await Resource.GetValueAsync(context, cancellationToken).ConfigureAwait(false);

if (string.IsNullOrEmpty(value) && !Optional)
{
Expand Down
42 changes: 17 additions & 25 deletions src/Aspire.Hosting/ApplicationModel/EndpointReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public sealed class EndpointReference : IManifestExpressionProvider, IValueProvi
// A reference to the endpoint annotation if it exists.
private EndpointAnnotation? _endpointAnnotation;
private bool? _isAllocated;
private readonly NetworkIdentifier _contextNetworkID;
private readonly NetworkIdentifier? _contextNetworkID;

/// <summary>
/// Gets the endpoint annotation associated with the endpoint reference.
Expand Down Expand Up @@ -71,7 +71,7 @@ public sealed class EndpointReference : IManifestExpressionProvider, IValueProvi
/// The reference will be resolved in the context of this network, which may be different
/// from the network associated with the default network of the referenced Endpoint.
/// </summary>
public NetworkIdentifier ContextNetworkID => _contextNetworkID;
public NetworkIdentifier? ContextNetworkID => _contextNetworkID;

/// <summary>
/// Gets the specified property expression of the endpoint. Defaults to the URL if no property is specified.
Expand Down Expand Up @@ -156,7 +156,7 @@ public EndpointReferenceExpression Property(EndpointProperty property)

foreach (var nes in endpointAnnotation.AllAllocatedEndpoints)
{
if (StringComparers.NetworkID.Equals(nes.NetworkID, _contextNetworkID))
if (StringComparers.NetworkID.Equals(nes.NetworkID, _contextNetworkID ?? KnownNetworkIdentifiers.LocalhostNetwork))
{
if (!nes.Snapshot.IsValueSet)
{
Expand All @@ -177,20 +177,20 @@ public EndpointReferenceExpression Property(EndpointProperty property)
/// <param name="endpoint">The endpoint annotation.</param>
/// <param name="contextNetworkID">The ID of the network that serves as the context for the EndpointReference.</param>
/// <remarks>
/// Most Aspire resources are accessed in the context of the "localhost" network (host proceses calling other host processes,
/// or host processes calling container via mapped ports). This is why EndpointReference assumes this
/// context unless specified otherwise. However, for container-to-container, or container-to-host communication,
/// you must specify a container network context for the EndpointReference to be resolved correctly.
/// Most Aspire resources are accessed in the context of the "localhost" network (host processes calling other host processes,
/// or host processes calling container via mapped ports). If a <see cref="NetworkIdentifier"/> is specified, the <see cref="EndpointReference"/>
/// will always resolve in the context of that network. If the <see cref="NetworkIdentifier"/> is null, the reference will attempt to resolve itself
/// based on the context of the requesting resource.
/// </remarks>
public EndpointReference(IResourceWithEndpoints owner, EndpointAnnotation endpoint, NetworkIdentifier? contextNetworkID = null)
public EndpointReference(IResourceWithEndpoints owner, EndpointAnnotation endpoint, NetworkIdentifier? contextNetworkID)
{
ArgumentNullException.ThrowIfNull(owner);
ArgumentNullException.ThrowIfNull(endpoint);

Resource = owner;
EndpointName = endpoint.Name;
_endpointAnnotation = endpoint;
_contextNetworkID = contextNetworkID ?? KnownNetworkIdentifiers.LocalhostNetwork;
_contextNetworkID = contextNetworkID;
}

/// <summary>
Expand Down Expand Up @@ -221,7 +221,7 @@ public EndpointReference(IResourceWithEndpoints owner, string endpointName, Netw

Resource = owner;
EndpointName = endpointName;
_contextNetworkID = contextNetworkID ?? KnownNetworkIdentifiers.LocalhostNetwork;
_contextNetworkID = contextNetworkID;
}

/// <summary>
Expand Down Expand Up @@ -265,7 +265,7 @@ public class EndpointReferenceExpression(EndpointReference endpointReference, En
/// <exception cref="InvalidOperationException">Throws when the selected <see cref="EndpointProperty"/> enumeration is not known.</exception>
public ValueTask<string?> GetValueAsync(CancellationToken cancellationToken = default)
{
return GetNetworkValueAsync(null, cancellationToken);
return GetValueAsync(new(), cancellationToken);
}

/// <summary>
Expand All @@ -275,33 +275,25 @@ public class EndpointReferenceExpression(EndpointReference endpointReference, En
/// <param name="cancellationToken">A <see cref="CancellationToken"/>.</param>
/// <returns>A <see cref="string"/> containing the selected <see cref="EndpointProperty"/> value.</returns>
/// <exception cref="InvalidOperationException">Throws when the selected <see cref="EndpointProperty"/> enumeration is not known.</exception>
public ValueTask<string?> GetValueAsync(ValueProviderContext context, CancellationToken cancellationToken = default)
public async ValueTask<string?> GetValueAsync(ValueProviderContext context, CancellationToken cancellationToken = default)
{
return context.Network switch
{
NetworkIdentifier networkID => GetNetworkValueAsync(networkID, cancellationToken),
_ => GetNetworkValueAsync(null, cancellationToken)
};
}
// If the EndpointReference was for a specific network context, then use that. Otherwise, use the network context from the ValueProviderContext.
// This allows the EndpointReference to be resolved in the context of the caller's network if it was not explicitly set.
var networkContext = Endpoint.ContextNetworkID ?? context.GetNetworkIdentifier();


private async ValueTask<string?> GetNetworkValueAsync(NetworkIdentifier? context, CancellationToken cancellationToken = default)
{
return Property switch
{
EndpointProperty.Scheme => new(Endpoint.Scheme),
EndpointProperty.IPV4Host when context is null || context == KnownNetworkIdentifiers.LocalhostNetwork => "127.0.0.1",
EndpointProperty.IPV4Host when networkContext == KnownNetworkIdentifiers.LocalhostNetwork => "127.0.0.1",
EndpointProperty.TargetPort when Endpoint.TargetPort is int port => new(port.ToString(CultureInfo.InvariantCulture)),
_ => await ResolveValueWithAllocatedAddress().ConfigureAwait(false)
};

async ValueTask<string?> ResolveValueWithAllocatedAddress()
{
var effectiveContext = context ?? Endpoint.ContextNetworkID;

// We are going to take the first snapshot that matches the context network ID. In general there might be multiple endpoints for a single service,
// and in future we might need some sort of policy to choose between them, but for now we just take the first one.
var nes = Endpoint.EndpointAnnotation.AllAllocatedEndpoints.Where(nes => nes.NetworkID == effectiveContext).FirstOrDefault();
var nes = Endpoint.EndpointAnnotation.AllAllocatedEndpoints.Where(nes => nes.NetworkID == networkContext).FirstOrDefault();
if (nes is null)
{
return null;
Expand Down
5 changes: 3 additions & 2 deletions src/Aspire.Hosting/ApplicationModel/ExpressionResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,14 @@ async Task<ResolvedValue> ResolveConnectionStringReferenceAsync(ConnectionString
/// </summary>
async ValueTask<ResolvedValue> ResolveInternalAsync(object? value, ValueProviderContext context)
{
var networkContext = context.GetNetworkIdentifier();
return value switch
{
ConnectionStringReference cs => await ResolveConnectionStringReferenceAsync(cs, context).ConfigureAwait(false),
IResourceWithConnectionString cs and not ConnectionStringParameterResource => await ResolveInternalAsync(cs.ConnectionStringExpression, context).ConfigureAwait(false),
ReferenceExpression ex => await EvalExpressionAsync(ex, context).ConfigureAwait(false),
EndpointReference er when context.Network == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(er, EndpointProperty.Url, context).ConfigureAwait(false), false),
EndpointReferenceExpression ep when context.Network == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(ep.Endpoint, ep.Property, context).ConfigureAwait(false), false),
EndpointReference er when networkContext == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(er, EndpointProperty.Url, context).ConfigureAwait(false), false),
EndpointReferenceExpression ep when networkContext == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(ep.Endpoint, ep.Property, context).ConfigureAwait(false), false),
IValueProvider vp => await EvalValueProvider(vp, context).ConfigureAwait(false),
_ => throw new NotImplementedException()
};
Expand Down
Loading