Skip to content

Commit

Permalink
Service Discovery: Implement approved API (#3413)
Browse files Browse the repository at this point in the history
* Rename EndPoint to Endpoint, resolver to provider

* Apply changes decided during API review

* Find & fix straggler file names

* Variables and members assignable to System.Net.EndPoint use upper-case P

* Update src/Microsoft.Extensions.ServiceDiscovery.Abstractions/ServiceEndpoint.cs

Co-authored-by: Stephen Halter <[email protected]>

* Delete GetEndpointString()

---------

Co-authored-by: Stephen Halter <[email protected]>
  • Loading branch information
ReubenBond and halter73 authored Apr 5, 2024
1 parent 81e680e commit b6c5eb4
Show file tree
Hide file tree
Showing 52 changed files with 763 additions and 810 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@
namespace Microsoft.Extensions.ServiceDiscovery;

/// <summary>
/// Builder to create a <see cref="ServiceEndPointSource"/> instances.
/// Builder to create a <see cref="ServiceEndpointSource"/> instances.
/// </summary>
public interface IServiceEndPointBuilder
public interface IServiceEndpointBuilder
{
/// <summary>
/// Gets the endpoints.
/// </summary>
IList<ServiceEndPoint> EndPoints { get; }
IList<ServiceEndpoint> Endpoints { get; }

/// <summary>
/// Gets the feature collection.
/// </summary>
IFeatureCollection Features { get; }

/// <summary>
/// Adds a change token to the resulting <see cref="ServiceEndPointSource"/>.
/// Adds a change token to the resulting <see cref="ServiceEndpointSource"/>.
/// </summary>
/// <param name="changeToken">The change token.</param>
void AddChangeToken(IChangeToken changeToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ namespace Microsoft.Extensions.ServiceDiscovery;
/// <summary>
/// Provides details about a service's endpoints.
/// </summary>
public interface IServiceEndPointProvider : IAsyncDisposable
public interface IServiceEndpointProvider : IAsyncDisposable
{
/// <summary>
/// Resolves the endpoints for the service.
/// </summary>
/// <param name="endPoints">The endpoint collection, which resolved endpoints will be added to.</param>
/// <param name="endpoints">The endpoint collection, which resolved endpoints will be added to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The resolution status.</returns>
ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken);
ValueTask PopulateAsync(IServiceEndpointBuilder endpoints, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;

namespace Microsoft.Extensions.ServiceDiscovery;

/// <summary>
/// Creates <see cref="IServiceEndpointProvider"/> instances.
/// </summary>
public interface IServiceEndpointProviderFactory
{
/// <summary>
/// Tries to create an <see cref="IServiceEndpointProvider"/> instance for the specified <paramref name="query"/>.
/// </summary>
/// <param name="query">The service to create the provider for.</param>
/// <param name="provider">The provider.</param>
/// <returns><see langword="true"/> if the provider was created, <see langword="false"/> otherwise.</returns>
bool TryCreateProvider(ServiceEndpointQuery query, [NotNullWhen(true)] out IServiceEndpointProvider? provider);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@

namespace Microsoft.Extensions.ServiceDiscovery.Internal;

internal sealed class ServiceEndPointImpl(EndPoint endPoint, IFeatureCollection? features = null) : ServiceEndPoint
internal sealed class ServiceEndpointImpl(EndPoint endPoint, IFeatureCollection? features = null) : ServiceEndpoint
{
public override EndPoint EndPoint { get; } = endPoint;
public override IFeatureCollection Features { get; } = features ?? new FeatureCollection();
public override string? ToString() => GetEndPointString();
public override string? ToString() => EndPoint switch
{
DnsEndPoint dns => $"{dns.Host}:{dns.Port}",
_ => EndPoint.ToString()!
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Microsoft.Extensions.ServiceDiscovery.Abstractions

The `Microsoft.Extensions.ServiceDiscovery.Abstractions` library provides abstractions used by the `Microsoft.Extensions.ServiceDiscovery` library and other libraries which implement service discovery extensions, such as service endpoint resolvers. For more information, see [Service discovery in .NET](https://learn.microsoft.com/dotnet/core/extensions/service-discovery).
The `Microsoft.Extensions.ServiceDiscovery.Abstractions` library provides abstractions used by the `Microsoft.Extensions.ServiceDiscovery` library and other libraries which implement service discovery extensions, such as service endpoint providers. For more information, see [Service discovery in .NET](https://learn.microsoft.com/dotnet/core/extensions/service-discovery).

## Feedback & contributing

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Net;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.ServiceDiscovery.Internal;
Expand All @@ -11,8 +10,7 @@ namespace Microsoft.Extensions.ServiceDiscovery;
/// <summary>
/// Represents an endpoint for a service.
/// </summary>
[DebuggerDisplay("{GetEndPointString(),nq}")]
public abstract class ServiceEndPoint
public abstract class ServiceEndpoint
{
/// <summary>
/// Gets the endpoint.
Expand All @@ -25,21 +23,10 @@ public abstract class ServiceEndPoint
public abstract IFeatureCollection Features { get; }

/// <summary>
/// Creates a new <see cref="ServiceEndPoint"/>.
/// Creates a new <see cref="ServiceEndpoint"/>.
/// </summary>
/// <param name="endPoint">The endpoint being represented.</param>
/// <param name="features">Features of the endpoint.</param>
/// <returns>A newly initialized <see cref="ServiceEndPoint"/>.</returns>
public static ServiceEndPoint Create(EndPoint endPoint, IFeatureCollection? features = null) => new ServiceEndPointImpl(endPoint, features);

/// <summary>
/// Gets a string representation of the <see cref="EndPoint"/>.
/// </summary>
/// <returns>A string representation of the <see cref="EndPoint"/>.</returns>
public virtual string GetEndPointString() => EndPoint switch
{
DnsEndPoint dns => $"{dns.Host}:{dns.Port}",
IPEndPoint ip => ip.ToString(),
_ => EndPoint.ToString()!
};
/// <returns>A newly initialized <see cref="ServiceEndpoint"/>.</returns>
public static ServiceEndpoint Create(EndPoint endPoint, IFeatureCollection? features = null) => new ServiceEndpointImpl(endPoint, features);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ namespace Microsoft.Extensions.ServiceDiscovery;
/// <summary>
/// Describes a query for endpoints of a service.
/// </summary>
public sealed class ServiceEndPointQuery
public sealed class ServiceEndpointQuery
{
private readonly string _originalString;

/// <summary>
/// Initializes a new <see cref="ServiceEndPointQuery"/> instance.
/// Initializes a new <see cref="ServiceEndpointQuery"/> instance.
/// </summary>
/// <param name="originalString">The string which the query was constructed from.</param>
/// <param name="includedSchemes">The ordered list of included URI schemes.</param>
/// <param name="serviceName">The service name.</param>
/// <param name="endPointName">The optional endpoint name.</param>
private ServiceEndPointQuery(string originalString, string[] includedSchemes, string serviceName, string? endPointName)
/// <param name="endpointName">The optional endpoint name.</param>
private ServiceEndpointQuery(string originalString, string[] includedSchemes, string serviceName, string? endpointName)
{
OriginalString = originalString;
IncludeSchemes = includedSchemes;
_originalString = originalString;
IncludedSchemes = includedSchemes;
ServiceName = serviceName;
EndPointName = endPointName;
EndpointName = endpointName;
}

/// <summary>
Expand All @@ -31,7 +33,7 @@ private ServiceEndPointQuery(string originalString, string[] includedSchemes, st
/// <param name="input">The value to parse.</param>
/// <param name="query">The resulting query.</param>
/// <returns><see langword="true"/> if the value was successfully parsed; otherwise <see langword="false"/>.</returns>
public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndPointQuery? query)
public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndpointQuery? query)
{
bool hasScheme;
if (!input.Contains("://", StringComparison.InvariantCulture)
Expand All @@ -52,10 +54,10 @@ public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndPoin
var uriHost = uri.Host;
var segmentSeparatorIndex = uriHost.IndexOf('.');
string host;
string? endPointName = null;
string? endpointName = null;
if (uriHost.StartsWith('_') && segmentSeparatorIndex > 1 && uriHost[^1] != '.')
{
endPointName = uriHost[1..segmentSeparatorIndex];
endpointName = uriHost[1..segmentSeparatorIndex];

// Skip the endpoint name, including its prefix ('_') and suffix ('.').
host = uriHost[(segmentSeparatorIndex + 1)..];
Expand All @@ -67,31 +69,26 @@ public static bool TryParse(string input, [NotNullWhen(true)] out ServiceEndPoin

// Allow multiple schemes to be separated by a '+', eg. "https+http://host:port".
var schemes = hasScheme ? uri.Scheme.Split('+') : [];
query = new(input, schemes, host, endPointName);
query = new(input, schemes, host, endpointName);
return true;
}

/// <summary>
/// Gets the string which the query was constructed from.
/// </summary>
public string OriginalString { get; }

/// <summary>
/// Gets the ordered list of included URI schemes.
/// </summary>
public IReadOnlyList<string> IncludeSchemes { get; }
public IReadOnlyList<string> IncludedSchemes { get; }

/// <summary>
/// Gets the endpoint name, or <see langword="null"/> if no endpoint name is specified.
/// </summary>
public string? EndPointName { get; }
public string? EndpointName { get; }

/// <summary>
/// Gets the service name.
/// </summary>
public string ServiceName { get; }

/// <inheritdoc/>
public override string? ToString() => EndPointName is not null ? $"Service: {ServiceName}, Endpoint: {EndPointName}, Schemes: {string.Join(", ", IncludeSchemes)}" : $"Service: {ServiceName}, Schemes: {string.Join(", ", IncludeSchemes)}";
public override string? ToString() => _originalString;
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ namespace Microsoft.Extensions.ServiceDiscovery;
/// Represents a collection of service endpoints.
/// </summary>
[DebuggerDisplay("{ToString(),nq}")]
[DebuggerTypeProxy(typeof(ServiceEndPointCollectionDebuggerView))]
public sealed class ServiceEndPointSource
[DebuggerTypeProxy(typeof(ServiceEndpointCollectionDebuggerView))]
public sealed class ServiceEndpointSource
{
private readonly List<ServiceEndPoint>? _endpoints;
private readonly List<ServiceEndpoint>? _endpoints;

/// <summary>
/// Initializes a new <see cref="ServiceEndPointSource"/> instance.
/// Initializes a new <see cref="ServiceEndpointSource"/> instance.
/// </summary>
/// <param name="endpoints">The endpoints.</param>
/// <param name="changeToken">The change token.</param>
/// <param name="features">The feature collection.</param>
public ServiceEndPointSource(List<ServiceEndPoint>? endpoints, IChangeToken changeToken, IFeatureCollection features)
public ServiceEndpointSource(List<ServiceEndpoint>? endpoints, IChangeToken changeToken, IFeatureCollection features)
{
ArgumentNullException.ThrowIfNull(changeToken);

Expand All @@ -34,7 +34,7 @@ public ServiceEndPointSource(List<ServiceEndPoint>? endpoints, IChangeToken chan
/// <summary>
/// Gets the endpoints.
/// </summary>
public IReadOnlyList<ServiceEndPoint> EndPoints => _endpoints ?? (IReadOnlyList<ServiceEndPoint>)[];
public IReadOnlyList<ServiceEndpoint> Endpoints => _endpoints ?? (IReadOnlyList<ServiceEndpoint>)[];

/// <summary>
/// Gets the change token which indicates when this collection should be refreshed.
Expand All @@ -57,13 +57,13 @@ public override string ToString()
return $"[{string.Join(", ", eps)}]";
}

private sealed class ServiceEndPointCollectionDebuggerView(ServiceEndPointSource value)
private sealed class ServiceEndpointCollectionDebuggerView(ServiceEndpointSource value)
{
public IChangeToken ChangeToken => value.ChangeToken;

public IFeatureCollection Features => value.Features;

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public ServiceEndPoint[] EndPoints => value.EndPoints.ToArray();
public ServiceEndpoint[] Endpoints => value.Endpoints.ToArray();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

namespace Microsoft.Extensions.ServiceDiscovery.Dns;

internal sealed partial class DnsServiceEndPointResolver(
string serviceName,
internal sealed partial class DnsServiceEndpointProvider(
ServiceEndpointQuery query,
string hostName,
IOptionsMonitor<DnsServiceEndPointResolverOptions> options,
ILogger<DnsServiceEndPointResolver> logger,
TimeProvider timeProvider) : DnsServiceEndPointResolverBase(serviceName, logger, timeProvider), IHostNameFeature
IOptionsMonitor<DnsServiceEndpointProviderOptions> options,
ILogger<DnsServiceEndpointProvider> logger,
TimeProvider timeProvider) : DnsServiceEndpointProviderBase(query, logger, timeProvider), IHostNameFeature
{
protected override double RetryBackOffFactor => options.CurrentValue.RetryBackOffFactor;
protected override TimeSpan MinRetryPeriod => options.CurrentValue.MinRetryPeriod;
Expand All @@ -26,27 +26,27 @@ internal sealed partial class DnsServiceEndPointResolver(

protected override async Task ResolveAsyncCore()
{
var endPoints = new List<ServiceEndPoint>();
var endpoints = new List<ServiceEndpoint>();
var ttl = DefaultRefreshPeriod;
Log.AddressQuery(logger, ServiceName, hostName);
var addresses = await System.Net.Dns.GetHostAddressesAsync(hostName, ShutdownToken).ConfigureAwait(false);
foreach (var address in addresses)
{
var serviceEndPoint = ServiceEndPoint.Create(new IPEndPoint(address, 0));
serviceEndPoint.Features.Set<IServiceEndPointProvider>(this);
if (options.CurrentValue.ApplyHostNameMetadata(serviceEndPoint))
var serviceEndpoint = ServiceEndpoint.Create(new IPEndPoint(address, 0));
serviceEndpoint.Features.Set<IServiceEndpointProvider>(this);
if (options.CurrentValue.ShouldApplyHostNameMetadata(serviceEndpoint))
{
serviceEndPoint.Features.Set<IHostNameFeature>(this);
serviceEndpoint.Features.Set<IHostNameFeature>(this);
}

endPoints.Add(serviceEndPoint);
endpoints.Add(serviceEndpoint);
}

if (endPoints.Count == 0)
if (endpoints.Count == 0)
{
throw new InvalidOperationException($"No DNS records were found for service {ServiceName} (DNS name: {hostName}).");
throw new InvalidOperationException($"No DNS records were found for service '{ServiceName}' (DNS name: '{hostName}').");
}

SetResult(endPoints, ttl);
SetResult(endpoints, ttl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Microsoft.Extensions.ServiceDiscovery.Dns;

partial class DnsServiceEndPointResolverBase
partial class DnsServiceEndpointProviderBase
{
internal static partial class Log
{
Expand Down
Loading

0 comments on commit b6c5eb4

Please sign in to comment.