Skip to content

Commit d1a85a3

Browse files
[release/13.0] Allow HostUrl to remap both address and port (#12552)
* Allow HostUrl to remap both address and port * Fix method signature in test * Check correct context value * Fix failing tests * Remove compatibility suppression that no longer applies * Fix more tests * REmove unused code * Add comments and enable tunnel in yarp playground * Update test helper to ask for specific endpoint * Ensure allocated endpoint checks still work the same * Updated method compat * Optional method * Don't add ambiguous method * Add additional comments and move config to appsettings --------- Co-authored-by: David Negstad <[email protected]> Co-authored-by: David Negstad <[email protected]>
1 parent ec32342 commit d1a85a3

21 files changed

+230
-146
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"DcpPublisher": {
3+
"EnableAspireContainerTunnel": true
4+
},
5+
"Logging": {
6+
"LogLevel": {
7+
"Default": "Information",
8+
"Microsoft.AspNetCore": "Warning",
9+
"Aspire.Hosting.Dcp": "Warning"
10+
}
11+
}
12+
}

src/Aspire.Hosting.Testing/DistributedApplicationHostingTestingExtensions.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,25 @@ public static Uri GetEndpoint(this DistributedApplication app, string resourceNa
6868
ArgumentNullException.ThrowIfNull(app);
6969
ArgumentException.ThrowIfNullOrEmpty(resourceName);
7070

71-
return new(GetEndpointUriStringCore(app, resourceName, endpointName));
71+
return GetEndpointForNetwork(app, resourceName, null, endpointName);
72+
}
73+
74+
/// <summary>
75+
/// Gets the endpoint for the specified resource.
76+
/// </summary>
77+
/// <param name="app">The application.</param>
78+
/// <param name="resourceName">The resource name.</param>
79+
/// <param name="networkIdentifier">The optional network identifier. If none is specified, the default network is used.</param>
80+
/// <param name="endpointName">The optional endpoint name. If none are specified, the single defined endpoint is returned.</param>
81+
/// <returns>A URI representation of the endpoint.</returns>
82+
/// <exception cref="ArgumentException">The resource was not found, no matching endpoint was found, or multiple endpoints were found.</exception>
83+
/// <exception cref="InvalidOperationException">The resource has no endpoints.</exception>
84+
public static Uri GetEndpointForNetwork(this DistributedApplication app, string resourceName, NetworkIdentifier? networkIdentifier, string? endpointName = default)
85+
{
86+
ArgumentNullException.ThrowIfNull(app);
87+
ArgumentException.ThrowIfNullOrEmpty(resourceName);
88+
89+
return new(GetEndpointUriStringCore(app, resourceName, endpointName, networkIdentifier));
7290
}
7391

7492
static IResource GetResource(DistributedApplication app, string resourceName)
@@ -87,7 +105,7 @@ static IResource GetResource(DistributedApplication app, string resourceName)
87105
return resource;
88106
}
89107

90-
static string GetEndpointUriStringCore(DistributedApplication app, string resourceName, string? endpointName = default)
108+
static string GetEndpointUriStringCore(DistributedApplication app, string resourceName, string? endpointName = default, NetworkIdentifier? networkIdentifier = default)
91109
{
92110
var resource = GetResource(app, resourceName);
93111
if (resource is not IResourceWithEndpoints resourceWithEndpoints)
@@ -98,11 +116,11 @@ static string GetEndpointUriStringCore(DistributedApplication app, string resour
98116
EndpointReference? endpoint;
99117
if (!string.IsNullOrEmpty(endpointName))
100118
{
101-
endpoint = GetEndpointOrDefault(resourceWithEndpoints, endpointName);
119+
endpoint = GetEndpointOrDefault(resourceWithEndpoints, endpointName, networkIdentifier);
102120
}
103121
else
104122
{
105-
endpoint = GetEndpointOrDefault(resourceWithEndpoints, "http") ?? GetEndpointOrDefault(resourceWithEndpoints, "https");
123+
endpoint = GetEndpointOrDefault(resourceWithEndpoints, "http", networkIdentifier) ?? GetEndpointOrDefault(resourceWithEndpoints, "https", networkIdentifier);
106124
}
107125

108126
if (endpoint is null)
@@ -122,9 +140,9 @@ static void ThrowIfNotStarted(DistributedApplication app)
122140
}
123141
}
124142

125-
static EndpointReference? GetEndpointOrDefault(IResourceWithEndpoints resourceWithEndpoints, string endpointName)
143+
static EndpointReference? GetEndpointOrDefault(IResourceWithEndpoints resourceWithEndpoints, string endpointName, NetworkIdentifier? networkIdentifier = default)
126144
{
127-
var reference = resourceWithEndpoints.GetEndpoint(endpointName);
145+
var reference = resourceWithEndpoints.GetEndpoint(endpointName, networkIdentifier ?? KnownNetworkIdentifiers.LocalhostNetwork);
128146

129147
return reference.IsAllocated ? reference : null;
130148
}

src/Aspire.Hosting/ApplicationModel/ConnectionStringReference.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,12 @@ public class ConnectionStringReference(IResourceWithConnectionString resource, b
2323

2424
ValueTask<string?> IValueProvider.GetValueAsync(CancellationToken cancellationToken)
2525
{
26-
return this.GetNetworkValueAsync(null, cancellationToken);
26+
return Resource.GetValueAsync(cancellationToken);
2727
}
2828

29-
ValueTask<string?> IValueProvider.GetValueAsync(ValueProviderContext context, CancellationToken cancellationToken)
29+
async ValueTask<string?> IValueProvider.GetValueAsync(ValueProviderContext context, CancellationToken cancellationToken)
3030
{
31-
return context.Network switch
32-
{
33-
NetworkIdentifier networkContext => GetNetworkValueAsync(networkContext, cancellationToken),
34-
_ => GetNetworkValueAsync(null, cancellationToken)
35-
};
36-
}
37-
38-
private async ValueTask<string?> GetNetworkValueAsync(NetworkIdentifier? networkContext, CancellationToken cancellationToken)
39-
{
40-
ValueProviderContext vpc = new() { Network = networkContext };
41-
var value = await Resource.GetValueAsync(vpc, cancellationToken).ConfigureAwait(false);
31+
var value = await Resource.GetValueAsync(context, cancellationToken).ConfigureAwait(false);
4232

4333
if (string.IsNullOrEmpty(value) && !Optional)
4434
{

src/Aspire.Hosting/ApplicationModel/EndpointReference.cs

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public sealed class EndpointReference : IManifestExpressionProvider, IValueProvi
1515
// A reference to the endpoint annotation if it exists.
1616
private EndpointAnnotation? _endpointAnnotation;
1717
private bool? _isAllocated;
18-
private readonly NetworkIdentifier _contextNetworkID;
18+
private readonly NetworkIdentifier? _contextNetworkID;
1919

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

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

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

190190
Resource = owner;
191191
EndpointName = endpoint.Name;
192192
_endpointAnnotation = endpoint;
193-
_contextNetworkID = contextNetworkID ?? KnownNetworkIdentifiers.LocalhostNetwork;
193+
_contextNetworkID = contextNetworkID;
194194
}
195195

196196
/// <summary>
@@ -221,7 +221,7 @@ public EndpointReference(IResourceWithEndpoints owner, string endpointName, Netw
221221

222222
Resource = owner;
223223
EndpointName = endpointName;
224-
_contextNetworkID = contextNetworkID ?? KnownNetworkIdentifiers.LocalhostNetwork;
224+
_contextNetworkID = contextNetworkID;
225225
}
226226

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

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

287-
288-
private async ValueTask<string?> GetNetworkValueAsync(NetworkIdentifier? context, CancellationToken cancellationToken = default)
289-
{
290284
return Property switch
291285
{
292286
EndpointProperty.Scheme => new(Endpoint.Scheme),
293-
EndpointProperty.IPV4Host when context is null || context == KnownNetworkIdentifiers.LocalhostNetwork => "127.0.0.1",
287+
EndpointProperty.IPV4Host when networkContext == KnownNetworkIdentifiers.LocalhostNetwork => "127.0.0.1",
294288
EndpointProperty.TargetPort when Endpoint.TargetPort is int port => new(port.ToString(CultureInfo.InvariantCulture)),
295289
_ => await ResolveValueWithAllocatedAddress().ConfigureAwait(false)
296290
};
297291

298292
async ValueTask<string?> ResolveValueWithAllocatedAddress()
299293
{
300-
var effectiveContext = context ?? Endpoint.ContextNetworkID;
301-
302294
// 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,
303295
// and in future we might need some sort of policy to choose between them, but for now we just take the first one.
304-
var nes = Endpoint.EndpointAnnotation.AllAllocatedEndpoints.Where(nes => nes.NetworkID == effectiveContext).FirstOrDefault();
296+
var nes = Endpoint.EndpointAnnotation.AllAllocatedEndpoints.Where(nes => nes.NetworkID == networkContext).FirstOrDefault();
305297
if (nes is null)
306298
{
307299
return null;

src/Aspire.Hosting/ApplicationModel/ExpressionResolver.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,14 @@ async Task<ResolvedValue> ResolveConnectionStringReferenceAsync(ConnectionString
9595
/// </summary>
9696
async ValueTask<ResolvedValue> ResolveInternalAsync(object? value, ValueProviderContext context)
9797
{
98+
var networkContext = context.GetNetworkIdentifier();
9899
return value switch
99100
{
100101
ConnectionStringReference cs => await ResolveConnectionStringReferenceAsync(cs, context).ConfigureAwait(false),
101102
IResourceWithConnectionString cs and not ConnectionStringParameterResource => await ResolveInternalAsync(cs.ConnectionStringExpression, context).ConfigureAwait(false),
102103
ReferenceExpression ex => await EvalExpressionAsync(ex, context).ConfigureAwait(false),
103-
EndpointReference er when context.Network == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(er, EndpointProperty.Url, context).ConfigureAwait(false), false),
104-
EndpointReferenceExpression ep when context.Network == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(ep.Endpoint, ep.Property, context).ConfigureAwait(false), false),
104+
EndpointReference er when networkContext == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(er, EndpointProperty.Url, context).ConfigureAwait(false), false),
105+
EndpointReferenceExpression ep when networkContext == KnownNetworkIdentifiers.DefaultAspireContainerNetwork => new ResolvedValue(await ResolveInContainerContextAsync(ep.Endpoint, ep.Property, context).ConfigureAwait(false), false),
105106
IValueProvider vp => await EvalValueProvider(vp, context).ConfigureAwait(false),
106107
_ => throw new NotImplementedException()
107108
};

0 commit comments

Comments
 (0)