-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
242e7bb
commit f3ba582
Showing
51 changed files
with
1,596 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Draft.Endpoints | ||
{ | ||
/// <summary> | ||
/// Represents a verified etcd endpoint. | ||
/// </summary> | ||
public sealed class Endpoint : IEquatable<Endpoint> | ||
{ | ||
|
||
/// <summary> | ||
/// Initializes a new <see cref="Endpoint" /> class with the specified <paramref name="availability" /> and | ||
/// <paramref name="uri" />. | ||
/// </summary> | ||
/// <param name="uri">The etcd endpoint uri.</param> | ||
/// <param name="availability">The etcd endpoint availability.</param> | ||
public Endpoint(Uri uri, EndpointAvailability availability) | ||
{ | ||
Uri = uri; | ||
Availability = availability; | ||
} | ||
|
||
/// <summary> | ||
/// <see cref="EndpointAvailability" /> value of this etcd endpoint. | ||
/// </summary> | ||
public EndpointAvailability Availability { get; private set; } | ||
|
||
/// <summary> | ||
/// Is <c>true</c> when <see cref="Availability" /><c> == </c><see cref="EndpointAvailability.Online" />. | ||
/// </summary> | ||
public bool IsOnline | ||
{ | ||
get { return Availability == EndpointAvailability.Online; } | ||
} | ||
|
||
/// <summary> | ||
/// <see cref="Uri" /> value of this etcd endpoint. | ||
/// </summary> | ||
public Uri Uri { get; private set; } | ||
|
||
/// <summary> | ||
/// Determines whether the specified <see cref="Endpoint" /> is equal to the current <see cref="Endpoint" />. | ||
/// </summary> | ||
public bool Equals(Endpoint other) | ||
{ | ||
if (ReferenceEquals(null, other)) { return false; } | ||
if (ReferenceEquals(this, other)) { return true; } | ||
return Availability == other.Availability && Equals(Uri, other.Uri); | ||
} | ||
|
||
/// <summary> | ||
/// Determines whether the specified object is equal to the current object. | ||
/// </summary> | ||
public override bool Equals(object obj) | ||
{ | ||
if (ReferenceEquals(null, obj)) { return false; } | ||
if (ReferenceEquals(this, obj)) { return true; } | ||
return obj is Endpoint && Equals((Endpoint) obj); | ||
} | ||
|
||
/// <summary> | ||
/// Serves as the default hash function. | ||
/// </summary> | ||
public override int GetHashCode() | ||
{ | ||
unchecked { return ((int) Availability * 397) ^ (Uri != null ? Uri.GetHashCode() : 0); } | ||
} | ||
|
||
/// <summary> | ||
/// Determines whether two specified <see cref="Endpoint" /> have the same value. | ||
/// </summary> | ||
public static bool operator ==(Endpoint left, Endpoint right) | ||
{ | ||
return Equals(left, right); | ||
} | ||
|
||
/// <summary> | ||
/// Determines whether two specified <see cref="Endpoint" /> have different values. | ||
/// </summary> | ||
public static bool operator !=(Endpoint left, Endpoint right) | ||
{ | ||
return !Equals(left, right); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Draft.Endpoints | ||
{ | ||
/// <summary> | ||
/// Etcd endpoint availability indicators | ||
/// </summary> | ||
public enum EndpointAvailability | ||
{ | ||
|
||
/// <summary> | ||
/// Endpoint availability is not known. | ||
/// </summary> | ||
Unknown, | ||
|
||
/// <summary> | ||
/// Endpoint is known to be correct and online. | ||
/// </summary> | ||
Online, | ||
|
||
/// <summary> | ||
/// Endpoint is known to be incorrect or offline. | ||
/// </summary> | ||
Offline | ||
|
||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
using Draft.Exceptions; | ||
|
||
namespace Draft.Endpoints | ||
{ | ||
public sealed partial class EndpointPool | ||
{ | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="EndpointPool.Builder" /> class. | ||
/// </summary> | ||
public static Builder Build() | ||
{ | ||
return new Builder(); | ||
} | ||
|
||
/// <summary> | ||
/// Helper class used to build a pool of etcd endpoints | ||
/// </summary> | ||
public sealed class Builder | ||
{ | ||
|
||
private EndpointRoutingStrategy _routingStrategy; | ||
|
||
private EndpointVerificationStrategy _verificationStrategy; | ||
|
||
internal EndpointRoutingStrategy RoutingStrategy | ||
{ | ||
get { return _routingStrategy ?? EndpointRoutingStrategy.Default; } | ||
} | ||
|
||
internal EndpointVerificationStrategy VerificationStrategy | ||
{ | ||
get { return _verificationStrategy ?? EndpointVerificationStrategy.Default; } | ||
} | ||
|
||
/// <summary> | ||
/// Verifies the passed <paramref name="uris" /> first to ensure that they are <see cref="Uri.IsAbsoluteUri" />. Then | ||
/// leverages the defined <see cref="EndpointVerificationStrategy" />. Finally creates an <see cref="EndpointPool" /> | ||
/// with | ||
/// the defined <see cref="EndpointRoutingStrategy" />. | ||
/// </summary> | ||
/// <param name="uris">Etcd endpoint uris to use.</param> | ||
/// <exception cref="ArgumentNullException">Passed <paramref name="uris" /> is null or empty.</exception> | ||
/// <exception cref="ArgumentException">One or more <paramref name="uris" /> is not an absolute <see cref="Uri" />.</exception> | ||
/// <exception cref="InvalidHostException"> | ||
/// May be thrown depending on the chosen | ||
/// <see cref="EndpointVerificationStrategy" />. | ||
/// </exception> | ||
public async Task<EndpointPool> VerifyAndBuild(params Uri[] uris) | ||
{ | ||
if (uris == null || !uris.Any()) | ||
{ | ||
throw new ArgumentNullException("uris", "You must supply at least 1 Uri"); | ||
} | ||
var invalidUris = uris.Where(x => !x.IsAbsoluteUri).ToList(); | ||
if (invalidUris.Any()) | ||
{ | ||
throw new ArgumentException( | ||
string.Format("The following Uri(s) are not valid absolute Uri(s): '{0}'", string.Join(", ", invalidUris)), | ||
"uris" | ||
); | ||
} | ||
var endpoints = await VerificationStrategy.Verify(uris); | ||
|
||
return new EndpointPool(endpoints, RoutingStrategy); | ||
} | ||
|
||
/// <summary> | ||
/// Defines the type of endpoint routing to use. Defaults to <see cref="EndpointRoutingStrategy.First" />. | ||
/// </summary> | ||
/// <exception cref="ArgumentNullException"><paramref name="routingStrategy" /> is <c>null</c>.</exception> | ||
public Builder WithRoutingStrategy(EndpointRoutingStrategy routingStrategy) | ||
{ | ||
if (routingStrategy == null) | ||
{ | ||
throw new ArgumentNullException("routingStrategy"); | ||
} | ||
_routingStrategy = routingStrategy; | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Defines the type of endpoint verification to use. Defaults to <see cref="EndpointVerificationStrategy.None" />. | ||
/// </summary> | ||
/// <exception cref="ArgumentNullException"><paramref name="verificationStrategy" /> is <c>null</c>.</exception> | ||
public Builder WithVerificationStrategy(EndpointVerificationStrategy verificationStrategy) | ||
{ | ||
if (verificationStrategy == null) | ||
{ | ||
throw new ArgumentNullException("verificationStrategy"); | ||
} | ||
_verificationStrategy = verificationStrategy; | ||
return this; | ||
} | ||
|
||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
using Flurl; | ||
|
||
namespace Draft.Endpoints | ||
{ | ||
/// <summary> | ||
/// Pool of Etcd Endpoints | ||
/// </summary> | ||
public sealed partial class EndpointPool | ||
{ | ||
|
||
internal EndpointPool(IEnumerable<Endpoint> endpoints, EndpointRoutingStrategy routingStrategy) | ||
{ | ||
AllEndpoints = endpoints.ToList(); | ||
RoutingStrategy = routingStrategy; | ||
} | ||
|
||
internal List<Endpoint> AllEndpoints { get; private set; } | ||
|
||
internal Endpoint[] OnlineEndpoints | ||
{ | ||
get { return AllEndpoints.Where(x => x.IsOnline).ToArray(); } | ||
} | ||
|
||
internal EndpointRoutingStrategy RoutingStrategy { get; private set; } | ||
|
||
internal Url GetEndpointUrl(params string[] pathParts) | ||
{ | ||
pathParts = pathParts ?? new string[0]; | ||
|
||
var keyPath = string.Join("/", pathParts.Select(x => x.TrimStart('/').TrimEnd('/'))); | ||
|
||
return RoutingStrategy.Select(keyPath, OnlineEndpoints).Uri.ToUrl() | ||
.AppendPathSegment(keyPath); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Draft.Endpoints | ||
{ | ||
/// <summary> | ||
/// Represents a strategy for selecting which etcd endpoint to use for http calls. | ||
/// </summary> | ||
public abstract class EndpointRoutingStrategy | ||
{ | ||
|
||
internal static EndpointRoutingStrategy Default | ||
{ | ||
get { return First; } | ||
} | ||
|
||
/// <summary> | ||
/// Executes the strategy against the passed <paramref name="key" /> and <paramref name="endpoints" />. | ||
/// </summary> | ||
/// <param name="key">The etcd key for the current http call.</param> | ||
/// <param name="endpoints">The <see cref="EndpointAvailability.Online" /> etcd endpoints.</param> | ||
/// <returns>The <see cref="Endpoint" /> to use in the current http call.</returns> | ||
public abstract Endpoint Select(string key, Endpoint[] endpoints); | ||
|
||
#region Built-in strategies | ||
|
||
private static readonly Lazy<EndpointRoutingStrategy> LazyConsistenKeyHashing = new Lazy<EndpointRoutingStrategy>(() => new RoutingStrategyConsistentKeyHashing()); | ||
|
||
private static readonly Lazy<EndpointRoutingStrategy> LazyFirst = new Lazy<EndpointRoutingStrategy>(() => new RoutingStrategyFirst()); | ||
|
||
private static readonly Lazy<EndpointRoutingStrategy> LazyRandom = new Lazy<EndpointRoutingStrategy>(() => new RoutingStrategyRandom()); | ||
|
||
private static readonly Lazy<EndpointRoutingStrategy> LazyRoundRobin = new Lazy<EndpointRoutingStrategy>(() => new RoutingStrategyRoundRobin()); | ||
|
||
// /// <summary> | ||
// /// Uses a consistent hashing algorithm on the etcd key to select the <see cref="Endpoint" />. | ||
// /// </summary> | ||
// public static EndpointRoutingStrategy ConsistentKeyHashing | ||
// { | ||
// get { return LazyConsistenKeyHashing.Value; } | ||
// } | ||
|
||
/// <summary> | ||
/// Uses the first <see cref="Endpoint" />. | ||
/// </summary> | ||
public static EndpointRoutingStrategy First | ||
{ | ||
get { return LazyFirst.Value; } | ||
} | ||
|
||
/// <summary> | ||
/// Uses a randomly selected <see cref="Endpoint" />. | ||
/// </summary> | ||
public static EndpointRoutingStrategy Random | ||
{ | ||
get { return LazyRandom.Value; } | ||
} | ||
|
||
/// <summary> | ||
/// Uses a round-robin patter for selecting the <see cref="Endpoint" />. | ||
/// </summary> | ||
public static EndpointRoutingStrategy RoundRobin | ||
{ | ||
get { return LazyRoundRobin.Value; } | ||
} | ||
|
||
#endregion | ||
} | ||
} |
Oops, something went wrong.