diff --git a/.paket/install.bat b/.paket/install.bat new file mode 100644 index 00000000..f7942c03 --- /dev/null +++ b/.paket/install.bat @@ -0,0 +1,7 @@ +@echo off +cd /d %~dp0 +paket.bootstrapper.exe +paket.exe install +echo. +echo. +pause \ No newline at end of file diff --git a/Gigya.Microdot.Configuration/Gigya.Microdot.Configuration.csproj b/Gigya.Microdot.Configuration/Gigya.Microdot.Configuration.csproj index 5acb387e..dc04f4e2 100644 --- a/Gigya.Microdot.Configuration/Gigya.Microdot.Configuration.csproj +++ b/Gigya.Microdot.Configuration/Gigya.Microdot.Configuration.csproj @@ -161,5 +161,4 @@ - \ No newline at end of file diff --git a/Gigya.Microdot.Fakes/DateTimeFake.cs b/Gigya.Microdot.Fakes/DateTimeFake.cs index 62c7ebed..73f1980b 100644 --- a/Gigya.Microdot.Fakes/DateTimeFake.cs +++ b/Gigya.Microdot.Fakes/DateTimeFake.cs @@ -22,14 +22,15 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Gigya.Microdot.Interfaces.SystemWrappers; namespace Gigya.Microdot.Fakes { - public class DateTimeFake: IDateTime + public class DateTimeFake : IDateTime { - public DateTime UtcNow { get; set; } + public DateTime UtcNow { get; set; } = DateTime.UtcNow; private TaskCompletionSource _delayTask = new TaskCompletionSource(); @@ -46,10 +47,27 @@ public DateTimeFake(bool manualDelay) _manualDelay = manualDelay; } - public Task Delay(TimeSpan delay) + public async Task Delay(TimeSpan delay, CancellationToken cancellationToken = default(CancellationToken)) { DelaysRequested.Add(delay); - return _manualDelay ? _delayTask.Task : Task.Delay(delay); + + if (_manualDelay) + await _delayTask.Task; + else + await Task.Delay(delay, cancellationToken); + + UtcNow += delay; + } + + public async Task DelayUntil(DateTime until, CancellationToken cancellationToken = default(CancellationToken)) + { + TimeSpan delayTime = until - UtcNow; + + if (delayTime > TimeSpan.Zero) + { + await Delay(delayTime, cancellationToken).ConfigureAwait(false); + UtcNow += delayTime; + } } /// diff --git a/Gigya.Microdot.Fakes/Discovery/AlwaysLocalHost.cs b/Gigya.Microdot.Fakes/Discovery/AlwaysLocalHost.cs index 329a69f0..8e1d1f1f 100644 --- a/Gigya.Microdot.Fakes/Discovery/AlwaysLocalHost.cs +++ b/Gigya.Microdot.Fakes/Discovery/AlwaysLocalHost.cs @@ -27,9 +27,9 @@ namespace Gigya.Microdot.Fakes.Discovery { public class AlwaysLocalHost : IDiscoverySourceLoader { - public IServiceDiscoverySource GetDiscoverySource(ServiceDeployment serviceDeployment, ServiceDiscoveryConfig serviceDiscoveryConfig) + public IServiceDiscoverySource GetDiscoverySource(DeploymentIdentifier deploymentIdentifier, ServiceDiscoveryConfig serviceDiscoveryConfig) { - return new LocalDiscoverySource(serviceDeployment); + return new LocalDiscoverySource(deploymentIdentifier); } } } \ No newline at end of file diff --git a/Gigya.Microdot.Fakes/Discovery/AlwaysLocalHostDiscovery.cs b/Gigya.Microdot.Fakes/Discovery/AlwaysLocalHostDiscovery.cs new file mode 100644 index 00000000..44d67352 --- /dev/null +++ b/Gigya.Microdot.Fakes/Discovery/AlwaysLocalHostDiscovery.cs @@ -0,0 +1,55 @@ +#region Copyright +// Copyright 2017 Gigya Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Threading.Tasks; +using Gigya.Microdot.ServiceDiscovery; +using Gigya.Microdot.ServiceDiscovery.Config; +using Gigya.Microdot.ServiceDiscovery.Rewrite; +using Gigya.Microdot.SharedLogic.Rewrite; + +namespace Gigya.Microdot.Fakes.Discovery +{ + public class AlwaysLocalhostDiscovery : IDiscovery + { + private Func CreateLoadBalancer {get;} + + public AlwaysLocalhostDiscovery(Func createLoadBalancer) + { + CreateLoadBalancer = createLoadBalancer; + } + + public async Task TryCreateLoadBalancer(DeploymentIdentifier deploymentIdentifier, ReachabilityCheck reachabilityCheck, TrafficRoutingStrategy trafficRoutingStrategy) + { + return CreateLoadBalancer(deploymentIdentifier, new LocalNodeSource(), reachabilityCheck, trafficRoutingStrategy); + } + + public async Task GetNodes(DeploymentIdentifier deploymentIdentifier) + { + return new LocalNodeSource().GetNodes(); + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Gigya.Microdot.Fakes/Discovery/LocalhostServiceDiscovery.cs b/Gigya.Microdot.Fakes/Discovery/LocalhostServiceDiscovery.cs index 082ce51f..acd1585f 100644 --- a/Gigya.Microdot.Fakes/Discovery/LocalhostServiceDiscovery.cs +++ b/Gigya.Microdot.Fakes/Discovery/LocalhostServiceDiscovery.cs @@ -20,31 +20,49 @@ // POSSIBILITY OF SUCH DAMAGE. #endregion +using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; -using Gigya.Microdot.Interfaces.HttpService; using Gigya.Microdot.ServiceDiscovery; +using Gigya.Microdot.ServiceDiscovery.Rewrite; +using Gigya.Microdot.SharedLogic.HttpService; +using Gigya.Microdot.SharedLogic.Rewrite; namespace Gigya.Microdot.Fakes.Discovery { - public class LocalhostServiceDiscovery : IServiceDiscovery + public class LocalhostServiceDiscovery : INewServiceDiscovery { - private static readonly IEndPointHandle handle = new LocalhostEndPointHandle(); + private readonly ILoadBalancer _localhostLoadBalancer = new LocalhostLoadBalancer(); - private readonly Task _source = Task.FromResult(handle); + public Task GetLoadBalancer() + { + return Task.FromResult(_localhostLoadBalancer); + } - private readonly Task allHosts = Task.FromResult(new[] { new EndPoint { HostName = handle.HostName, Port = handle.Port } }); + } + + public class LocalhostLoadBalancer : ILoadBalancer + { + readonly INodeSource _localNodeSource = new LocalNodeSource(); - public Task GetNextHost(string affinityToken = null) => _source; + public async Task GetNode() + { + return _localNodeSource.GetNodes().First(); + } - public Task GetOrWaitForNextHost(CancellationToken cancellationToken) => _source; + public Task WasUndeployed() => Task.FromResult(false); - public ISourceBlock EndPointsChanged => new BroadcastBlock(null); + public void ReportUnreachable(Node node, Exception ex = null) + { + } - public ISourceBlock ReachabilityChanged => new BroadcastBlock(null); + public void Dispose() + { - public Task GetAllEndPoints() => allHosts; + } } + } diff --git a/Gigya.Microdot.Fakes/Gigya.Microdot.Fakes.csproj b/Gigya.Microdot.Fakes/Gigya.Microdot.Fakes.csproj index 9836e256..c5f2f1d7 100644 --- a/Gigya.Microdot.Fakes/Gigya.Microdot.Fakes.csproj +++ b/Gigya.Microdot.Fakes/Gigya.Microdot.Fakes.csproj @@ -44,6 +44,7 @@ Properties\SolutionVersion.cs + @@ -127,5 +128,4 @@ - \ No newline at end of file diff --git a/Gigya.Microdot.Hosting/Events/ServiceCallEvent.cs b/Gigya.Microdot.Hosting/Events/ServiceCallEvent.cs index 25087d62..6b33d2e8 100644 --- a/Gigya.Microdot.Hosting/Events/ServiceCallEvent.cs +++ b/Gigya.Microdot.Hosting/Events/ServiceCallEvent.cs @@ -25,8 +25,8 @@ using System.Linq; using System.Text.RegularExpressions; using Gigya.Microdot.Interfaces.Events; -using Gigya.Microdot.Interfaces.HttpService; using Gigya.Microdot.SharedLogic.Events; +using Gigya.Microdot.SharedLogic.HttpService; namespace Gigya.Microdot.Hosting.Events { diff --git a/Gigya.Microdot.Hosting/Gigya.Microdot.Hosting.csproj b/Gigya.Microdot.Hosting/Gigya.Microdot.Hosting.csproj index 77b80032..85492f94 100644 --- a/Gigya.Microdot.Hosting/Gigya.Microdot.Hosting.csproj +++ b/Gigya.Microdot.Hosting/Gigya.Microdot.Hosting.csproj @@ -77,6 +77,7 @@ + @@ -195,5 +196,4 @@ - \ No newline at end of file diff --git a/Gigya.Microdot.Hosting/HttpService/Endpoints/SchemaEndpoint.cs b/Gigya.Microdot.Hosting/HttpService/Endpoints/SchemaEndpoint.cs index a20a4f1c..26959527 100644 --- a/Gigya.Microdot.Hosting/HttpService/Endpoints/SchemaEndpoint.cs +++ b/Gigya.Microdot.Hosting/HttpService/Endpoints/SchemaEndpoint.cs @@ -20,7 +20,6 @@ // POSSIBILITY OF SUCH DAMAGE. #endregion -using System.Linq; using System.Net; using System.Threading.Tasks; using Gigya.Common.Contracts.HttpService; @@ -28,16 +27,14 @@ namespace Gigya.Microdot.Hosting.HttpService.Endpoints { - public class SchemaEndpoint : ICustomEndpoint { private readonly string _jsonSchema; - public SchemaEndpoint(IServiceInterfaceMapper mapper) + public SchemaEndpoint(ServiceSchema schemaProvider) { - _jsonSchema = JsonConvert.SerializeObject(new ServiceSchema(mapper.ServiceInterfaceTypes.ToArray()), new JsonSerializerSettings{Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore}); - } - + _jsonSchema = JsonConvert.SerializeObject(schemaProvider, new JsonSerializerSettings{Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore}); + } public async Task TryHandle(HttpListenerContext context, WriteResponseDelegate writeResponse) { diff --git a/Gigya.Microdot.Hosting/HttpService/HttpServiceListener.cs b/Gigya.Microdot.Hosting/HttpService/HttpServiceListener.cs index 24da1c58..3f783541 100644 --- a/Gigya.Microdot.Hosting/HttpService/HttpServiceListener.cs +++ b/Gigya.Microdot.Hosting/HttpService/HttpServiceListener.cs @@ -33,16 +33,17 @@ using System.Threading.Tasks; using Gigya.Common.Contracts; using Gigya.Common.Contracts.Exceptions; +using Gigya.Common.Contracts.HttpService; using Gigya.Microdot.Hosting.Events; using Gigya.Microdot.Hosting.HttpService.Endpoints; using Gigya.Microdot.Interfaces.Configuration; using Gigya.Microdot.Interfaces.Events; -using Gigya.Microdot.Interfaces.HttpService; using Gigya.Microdot.Interfaces.Logging; using Gigya.Microdot.SharedLogic; using Gigya.Microdot.SharedLogic.Configurations; using Gigya.Microdot.SharedLogic.Events; using Gigya.Microdot.SharedLogic.Exceptions; +using Gigya.Microdot.SharedLogic.HttpService; using Gigya.Microdot.SharedLogic.Measurement; using Gigya.Microdot.SharedLogic.Security; using Metrics; @@ -87,6 +88,7 @@ public sealed class HttpServiceListener : IDisposable private JsonExceptionSerializer ExceptionSerializer { get; } private Func LoadSheddingConfig { get; } + private ServiceSchema ServiceSchema { get; } private readonly Timer _serializationTime; private readonly Timer _deserializationTime; @@ -100,9 +102,13 @@ public sealed class HttpServiceListener : IDisposable public HttpServiceListener(IActivator activator, IWorker worker, IServiceEndPointDefinition serviceEndPointDefinition, ICertificateLocator certificateLocator, ILog log, IEventPublisher eventPublisher, IEnumerable customEndpoints, IEnvironmentVariableProvider environmentVariableProvider, - IServerRequestPublisher serverRequestPublisher, - JsonExceptionSerializer exceptionSerializer, Func loadSheddingConfig) + JsonExceptionSerializer exceptionSerializer, + ServiceSchema serviceSchema, + Func loadSheddingConfig, + IServerRequestPublisher serverRequestPublisher) + { + ServiceSchema = serviceSchema; _serverRequestPublisher = serverRequestPublisher; ServiceEndPointDefinition = serviceEndPointDefinition; Worker = worker; @@ -356,12 +362,12 @@ private static IEnumerable GetAllExceptions(Exception ex) private void ValidateRequest(HttpListenerContext context) { - var clientVersion = context.Request.Headers[GigyaHttpHeaders.Version]; + var clientVersion = context.Request.Headers[GigyaHttpHeaders.ProtocolVersion]; - if (clientVersion != null && clientVersion != HttpServiceRequest.Version) + if (clientVersion != null && clientVersion != HttpServiceRequest.ProtocolVersion) { _failureCounter.Increment("ProtocolVersionMismatch"); - throw new RequestException($"Client protocol version {clientVersion} is not supported by the server protocol version {HttpServiceRequest.Version}."); + throw new RequestException($"Client protocol version {clientVersion} is not supported by the server protocol version {HttpServiceRequest.ProtocolVersion}."); } if (context.Request.HttpMethod != "POST") @@ -416,7 +422,7 @@ private async Task CheckSecureConnection(HttpListenerContext context) private async Task TryWriteResponse(HttpListenerContext context, string data, HttpStatusCode httpStatus = HttpStatusCode.OK, string contentType = "application/json") { - context.Response.Headers.Add(GigyaHttpHeaders.Version, HttpServiceRequest.Version); + context.Response.Headers.Add(GigyaHttpHeaders.ProtocolVersion, HttpServiceRequest.ProtocolVersion); var body = Encoding.UTF8.GetBytes(data ?? ""); @@ -427,6 +433,8 @@ private async Task TryWriteResponse(HttpListenerContext context, string data, Ht context.Response.Headers.Add(GigyaHttpHeaders.Environment, EnvironmentVariableProvider.DeploymentEnvironment); context.Response.Headers.Add(GigyaHttpHeaders.ServiceVersion, CurrentApplicationInfo.Version.ToString()); context.Response.Headers.Add(GigyaHttpHeaders.ServerHostname, CurrentApplicationInfo.HostName); + context.Response.Headers.Add(GigyaHttpHeaders.SchemaHash, ServiceSchema.Hash); + try { await context.Response.OutputStream.WriteAsync(body, 0, body.Length); diff --git a/Gigya.Microdot.Hosting/HttpService/IServiceEndPointDefinition.cs b/Gigya.Microdot.Hosting/HttpService/IServiceEndPointDefinition.cs index 9f036857..7748da12 100644 --- a/Gigya.Microdot.Hosting/HttpService/IServiceEndPointDefinition.cs +++ b/Gigya.Microdot.Hosting/HttpService/IServiceEndPointDefinition.cs @@ -23,7 +23,7 @@ using System; using System.Collections.Generic; using Gigya.Common.Contracts.HttpService; -using Gigya.Microdot.Interfaces.HttpService; +using Gigya.Microdot.SharedLogic.HttpService; namespace Gigya.Microdot.Hosting.HttpService { diff --git a/Gigya.Microdot.Hosting/HttpService/ServerRequestPublisher.cs b/Gigya.Microdot.Hosting/HttpService/ServerRequestPublisher.cs index 3f25809b..9dd1991f 100644 --- a/Gigya.Microdot.Hosting/HttpService/ServerRequestPublisher.cs +++ b/Gigya.Microdot.Hosting/HttpService/ServerRequestPublisher.cs @@ -5,9 +5,8 @@ using System.Linq; using Gigya.Microdot.Hosting.Events; using Gigya.Microdot.Interfaces.Events; -using Gigya.Microdot.Interfaces.HttpService; using Gigya.Microdot.SharedLogic.Events; -using Newtonsoft.Json; +using Gigya.Microdot.SharedLogic.HttpService; namespace Gigya.Microdot.Hosting.HttpService { diff --git a/Gigya.Microdot.Hosting/HttpService/ServiceEndPointDefinition.cs b/Gigya.Microdot.Hosting/HttpService/ServiceEndPointDefinition.cs index 9cf5114d..44b8ed3e 100644 --- a/Gigya.Microdot.Hosting/HttpService/ServiceEndPointDefinition.cs +++ b/Gigya.Microdot.Hosting/HttpService/ServiceEndPointDefinition.cs @@ -28,10 +28,10 @@ using Gigya.Common.Contracts.Exceptions; using Gigya.Common.Contracts.HttpService; using Gigya.Microdot.Interfaces; -using Gigya.Microdot.Interfaces.HttpService; using Gigya.Microdot.ServiceDiscovery.Config; using Gigya.Microdot.SharedLogic; using Gigya.Microdot.SharedLogic.Exceptions; +using Gigya.Microdot.SharedLogic.HttpService; namespace Gigya.Microdot.Hosting.HttpService { @@ -70,7 +70,7 @@ public ServiceEndPointDefinition(IServiceInterfaceMapper mapper, ServiceNames = serviceInterfaces .Where(i => i.GetCustomAttribute() != null) - .ToDictionary(x => x, x => x.GetCustomAttribute().Name ?? x.Name); + .ToDictionary(x => x, x => x.Name); var interfacePorts = serviceInterfaces.Select(i => { diff --git a/Gigya.Microdot.Hosting/HttpService/ServiceMethodResolver.cs b/Gigya.Microdot.Hosting/HttpService/ServiceMethodResolver.cs index 07e4b758..5ee6b13a 100644 --- a/Gigya.Microdot.Hosting/HttpService/ServiceMethodResolver.cs +++ b/Gigya.Microdot.Hosting/HttpService/ServiceMethodResolver.cs @@ -26,7 +26,7 @@ using System.Linq; using System.Reflection; using Gigya.Common.Contracts.Exceptions; -using Gigya.Microdot.Interfaces.HttpService; +using Gigya.Microdot.SharedLogic.HttpService; namespace Gigya.Microdot.Hosting.HttpService { diff --git a/Gigya.Microdot.Hosting/Validators/LogFieldAttributeValidator.cs b/Gigya.Microdot.Hosting/Validators/LogFieldAttributeValidator.cs index 7203b0ac..a625387f 100644 --- a/Gigya.Microdot.Hosting/Validators/LogFieldAttributeValidator.cs +++ b/Gigya.Microdot.Hosting/Validators/LogFieldAttributeValidator.cs @@ -1,4 +1,26 @@ -using System; +#region Copyright +// Copyright 2017 Gigya Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; using System.Linq; using System.Reflection; using Gigya.Common.Contracts.Exceptions; diff --git a/Gigya.Microdot.Hosting/Validators/SensitivityAttributesValidator.cs b/Gigya.Microdot.Hosting/Validators/SensitivityAttributesValidator.cs new file mode 100644 index 00000000..db18126f --- /dev/null +++ b/Gigya.Microdot.Hosting/Validators/SensitivityAttributesValidator.cs @@ -0,0 +1,90 @@ +#region Copyright +// Copyright 2017 Gigya Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Gigya.Common.Contracts.Exceptions; +using Gigya.Microdot.Hosting.HttpService; +using Gigya.ServiceContract.Attributes; + +namespace Gigya.Microdot.Hosting.Validators +{ + public class SensitivityAttributesValidator : IValidator + { + private readonly IServiceInterfaceMapper _serviceInterfaceMapper; + + public SensitivityAttributesValidator(IServiceInterfaceMapper serviceInterfaceMapper) + { + _serviceInterfaceMapper = serviceInterfaceMapper; + } + + public void Validate() + { + foreach (var serviceInterface in _serviceInterfaceMapper.ServiceInterfaceTypes) + { + foreach (var method in serviceInterface.GetMethods()) + { + if (method.GetCustomAttribute(typeof(SensitiveAttribute)) != null && method.GetCustomAttribute(typeof(NonSensitiveAttribute)) != null) + throw new ProgrammaticException($"[Sensitive] and [NonSensitive] can't both be applied on the same method ({method.Name}) on serviceInterface ({serviceInterface.Name})"); + + foreach (var parameter in method.GetParameters()) + { + if (parameter.GetCustomAttribute(typeof(SensitiveAttribute)) != null && parameter.GetCustomAttribute(typeof(NonSensitiveAttribute)) != null) + { + throw new ProgrammaticException($"[Sensitive] and [NonSensitive] can't both be applied on the same parameter ({parameter.Name}) in method ({method.Name}) on serviceInterface ({serviceInterface.Name})"); + } + + var logFieldExists = Attribute.IsDefined(parameter, typeof(LogFieldsAttribute)); + if (parameter.ParameterType.IsClass && parameter.ParameterType.FullName?.StartsWith("System.") == false) + VerifyMisplacedSensitiveAttribute(logFieldExists, method.Name, parameter.Name, parameter.ParameterType, new Stack()); + } + } + } + } + + + private void VerifyMisplacedSensitiveAttribute(bool logFieldExists, string methodName, string paramName, Type type, Stack path) + { + if (type.IsClass == false || type.FullName?.StartsWith("System.") == true) + return; + + foreach (var memberInfo in type.FindMembers(MemberTypes.Property | MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance, null, null) + .Where(x => x is FieldInfo || (x is PropertyInfo propertyInfo) && propertyInfo.CanRead)) + { + path.Push(memberInfo.Name); + + if (memberInfo.GetCustomAttribute(typeof(SensitiveAttribute)) != null || memberInfo.GetCustomAttribute(typeof(NonSensitiveAttribute)) != null) + if (!logFieldExists) + throw new ProgrammaticException($"The method '{methodName}' parameter '{paramName}' has a member '{string.Join(" --> ", path.Reverse())}' that is marked as [Sensitive] or [NonSensitive], but the method parameter is not marked with [LogFields]"); + else if (path.Count > 1) + throw new ProgrammaticException($"The method '{methodName}' parameter '{paramName}' has a member '{string.Join(" --> ", path.Reverse())}' that is marked as [Sensitive] or [NonSensitive], but only root-level members can be marked as such."); + + Type memberType = memberInfo is PropertyInfo propertyInfo ? propertyInfo.PropertyType : ((FieldInfo)memberInfo).FieldType; + VerifyMisplacedSensitiveAttribute(logFieldExists, methodName, paramName, memberType, path); + + path.Pop(); + } + } + } +} \ No newline at end of file diff --git a/Gigya.Microdot.Interfaces/Gigya.Microdot.Interfaces.csproj b/Gigya.Microdot.Interfaces/Gigya.Microdot.Interfaces.csproj index 343b3224..0bcef000 100644 --- a/Gigya.Microdot.Interfaces/Gigya.Microdot.Interfaces.csproj +++ b/Gigya.Microdot.Interfaces/Gigya.Microdot.Interfaces.csproj @@ -54,10 +54,6 @@ - - - - diff --git a/Gigya.Microdot.Interfaces/SystemWrappers/IDateTime.cs b/Gigya.Microdot.Interfaces/SystemWrappers/IDateTime.cs index 3b436ed7..001c92ee 100644 --- a/Gigya.Microdot.Interfaces/SystemWrappers/IDateTime.cs +++ b/Gigya.Microdot.Interfaces/SystemWrappers/IDateTime.cs @@ -21,6 +21,7 @@ #endregion using System; +using System.Threading; using System.Threading.Tasks; namespace Gigya.Microdot.Interfaces.SystemWrappers @@ -28,6 +29,8 @@ namespace Gigya.Microdot.Interfaces.SystemWrappers public interface IDateTime { DateTime UtcNow { get; } - Task Delay(TimeSpan delay); + Task Delay(TimeSpan delay, CancellationToken cancellationToken = default(CancellationToken)); + + Task DelayUntil(DateTime until, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/Gigya.Microdot.Ninject.Host/Gigya.Microdot.Ninject.Host.csproj b/Gigya.Microdot.Ninject.Host/Gigya.Microdot.Ninject.Host.csproj index a0ab5ca2..d943aa82 100644 --- a/Gigya.Microdot.Ninject.Host/Gigya.Microdot.Ninject.Host.csproj +++ b/Gigya.Microdot.Ninject.Host/Gigya.Microdot.Ninject.Host.csproj @@ -144,5 +144,4 @@ - \ No newline at end of file diff --git a/Gigya.Microdot.Ninject/Gigya.Microdot.Ninject.csproj b/Gigya.Microdot.Ninject/Gigya.Microdot.Ninject.csproj index df58b0a9..ca353d5b 100644 --- a/Gigya.Microdot.Ninject/Gigya.Microdot.Ninject.csproj +++ b/Gigya.Microdot.Ninject/Gigya.Microdot.Ninject.csproj @@ -186,5 +186,4 @@ - \ No newline at end of file diff --git a/Gigya.Microdot.Ninject/MicrodotModule.cs b/Gigya.Microdot.Ninject/MicrodotModule.cs index 2d5facee..a24bbac2 100644 --- a/Gigya.Microdot.Ninject/MicrodotModule.cs +++ b/Gigya.Microdot.Ninject/MicrodotModule.cs @@ -22,17 +22,24 @@ using System; using System.Collections.Concurrent; +using System.Linq; +using Gigya.Common.Contracts.HttpService; using Gigya.Microdot.Configuration; +using Gigya.Microdot.Hosting.HttpService; using Gigya.Microdot.ServiceDiscovery; using Gigya.Microdot.ServiceDiscovery.HostManagement; +using Gigya.Microdot.ServiceDiscovery.Rewrite; using Gigya.Microdot.ServiceProxy; using Gigya.Microdot.SharedLogic; using Gigya.Microdot.SharedLogic.Monitor; +using Gigya.Microdot.SharedLogic.Rewrite; using Metrics; using Ninject; using Ninject.Activation; using Ninject.Extensions.Factory; using Ninject.Modules; +using ConsulClient = Gigya.Microdot.ServiceDiscovery.ConsulClient; +using IConsulClient = Gigya.Microdot.ServiceDiscovery.IConsulClient; namespace Gigya.Microdot.Ninject { @@ -46,6 +53,7 @@ public class MicrodotModule : NinjectModule { typeof(ConsulDiscoverySource), typeof(RemoteHostPool), + typeof(LoadBalancer), typeof(ConfigDiscoverySource) }; @@ -60,10 +68,11 @@ public override void Load() Kernel.Load(); this.BindClassesAsSingleton(NonSingletonBaseTypes, typeof(ConfigurationAssembly), typeof(ServiceProxyAssembly)); - this.BindInterfacesAsSingleton(NonSingletonBaseTypes, typeof(ConfigurationAssembly), typeof(ServiceProxyAssembly), typeof(SharedLogicAssembly),typeof(ServiceDiscoveryAssembly)); - + this.BindInterfacesAsSingleton(NonSingletonBaseTypes, typeof(ConfigurationAssembly), typeof(ServiceProxyAssembly), typeof(SharedLogicAssembly), typeof(ServiceDiscoveryAssembly)); + Bind().ToFactory(); + Kernel.BindPerKey(); Kernel.BindPerKey(); Kernel.BindPerString(); Kernel.BindPerString(); @@ -76,9 +85,18 @@ public override void Load() Bind().To().InTransientScope(); Bind().To().InTransientScope(); + Rebind().To().InTransientScope(); + Rebind().To().InTransientScope(); + Rebind().To().InSingletonScope(); + + Rebind().ToSelf().InSingletonScope(); + Kernel.Rebind().To().InTransientScope(); Kernel.Load(); Kernel.Load(); + + Kernel.Rebind().ToMethod(c => + new ServiceSchema(c.Kernel.Get().ServiceInterfaceTypes.ToArray())).InSingletonScope(); } diff --git a/Gigya.Microdot.Orleans.Ninject.Host/Gigya.Microdot.Orleans.Ninject.Host.csproj b/Gigya.Microdot.Orleans.Ninject.Host/Gigya.Microdot.Orleans.Ninject.Host.csproj index 5d900353..72f8b158 100644 --- a/Gigya.Microdot.Orleans.Ninject.Host/Gigya.Microdot.Orleans.Ninject.Host.csproj +++ b/Gigya.Microdot.Orleans.Ninject.Host/Gigya.Microdot.Orleans.Ninject.Host.csproj @@ -227,5 +227,4 @@ - \ No newline at end of file diff --git a/Gigya.Microdot.ServiceDiscovery/Config/ConsulConfig.cs b/Gigya.Microdot.ServiceDiscovery/Config/ConsulConfig.cs index 6f1f2654..ad10d801 100644 --- a/Gigya.Microdot.ServiceDiscovery/Config/ConsulConfig.cs +++ b/Gigya.Microdot.ServiceDiscovery/Config/ConsulConfig.cs @@ -3,6 +3,7 @@ namespace Gigya.Microdot.ServiceDiscovery.Config { + [Serializable] [ConfigurationRoot("Consul", RootStrategy.ReplaceClassNameWithPath)] public class ConsulConfig : IConfigObject @@ -10,23 +11,32 @@ public class ConsulConfig : IConfigObject /// /// Whether to Call Consul with long-polling, waiting for changes to occur, or to call it periodically /// + [Obsolete("To be deleted after discovery refactoring")] public bool LongPolling { get; set; } = false; /// /// Interval for reloading endpoints from Consul, - /// Used only when LongPolling=false + /// Used for Consul queries loop /// public TimeSpan ReloadInterval { get; set; } = TimeSpan.FromSeconds(1); + /// + /// Timeout passed to Consul telling it when to break long-polling. + /// + public TimeSpan HttpTimeout { get; set; } = TimeSpan.FromMinutes(2); + /// /// Time to wait for http response from Consul. /// When LongPolling=true, defines the maximum time to wait on long-polling. /// When LongPolling=false, defines the timeout for Consul http requests. + /// We take a few seconds more than to reduce the + /// risk of getting task cancelled exceptions before Consul gracefully timed out, + /// due to network latency or the process being overloaded. /// - public TimeSpan HttpTimeout { get; set; } = TimeSpan.FromMinutes(2); + public TimeSpan HttpTaskTimeout => HttpTimeout.Add(TimeSpan.FromSeconds(5)); /// - /// Interval for retrying access to surce (e.g. Consul) after an error has occured + /// Interval for retrying access to Consul after an error has occured /// public TimeSpan ErrorRetryInterval { get; set; } = TimeSpan.FromSeconds(1); } diff --git a/Gigya.Microdot.ServiceDiscovery/Config/DiscoveryConfig.cs b/Gigya.Microdot.ServiceDiscovery/Config/DiscoveryConfig.cs index 7fae8f86..b00bf384 100644 --- a/Gigya.Microdot.ServiceDiscovery/Config/DiscoveryConfig.cs +++ b/Gigya.Microdot.ServiceDiscovery/Config/DiscoveryConfig.cs @@ -50,20 +50,28 @@ public class DiscoveryConfig : IConfigObject /// public TimeSpan? RequestTimeout { get; set; } + /// + /// Time period to keep monitoring a deployed service after it was no longer requested + /// + public TimeSpan MonitoringLifetime { get; set; } = TimeSpan.FromMinutes(5); + /// /// When we lose connection to some endpoint, we wait this delay till we start trying to reconnect. /// + [Obsolete("To be deleted after discovery refactoring")] public double FirstAttemptDelaySeconds { get; set; } = 0.001; /// /// When retrying to reconnect to an endpoint, we use exponential backoff (e.g. 1,2,4,8ms, etc). Once that /// backoff reaches this value, it won't increase any more. /// + [Obsolete("To be deleted after discovery refactoring")] public double MaxAttemptDelaySeconds { get; set; } = 10; /// /// The factor of the exponential backoff when retrying connections to endpoints. /// + [Obsolete("To be deleted after discovery refactoring")] public double DelayMultiplier { get; set; } = 2; /// diff --git a/Gigya.Microdot.ServiceDiscovery/Config/ServiceDiscoveryConfig.cs b/Gigya.Microdot.ServiceDiscovery/Config/ServiceDiscoveryConfig.cs index 2f6e8f9e..be63ef07 100644 --- a/Gigya.Microdot.ServiceDiscovery/Config/ServiceDiscoveryConfig.cs +++ b/Gigya.Microdot.ServiceDiscovery/Config/ServiceDiscoveryConfig.cs @@ -46,17 +46,20 @@ public class ServiceDiscoveryConfig /// /// When we lose connection to some endpoint, we wait this delay till we start trying to reconnect. /// + [Obsolete("To be deleted after discovery refactoring")] public double? FirstAttemptDelaySeconds { get; set; } /// /// When retrying to reconnect to an endpoint, we use exponential backoff (e.g. 1,2,4,8ms, etc). Once that /// backoff reaches this value, it won't increase any more. /// + [Obsolete("To be deleted after discovery refactoring")] public double? MaxAttemptDelaySeconds { get; set; } /// /// The factor of the exponential backoff when retrying connections to endpoints. /// + [Obsolete("To be deleted after discovery refactoring")] public double? DelayMultiplier { get; set; } /// diff --git a/Gigya.Microdot.ServiceDiscovery/ConfigDiscoverySource.cs b/Gigya.Microdot.ServiceDiscovery/ConfigDiscoverySource.cs index 931c34ec..3f2c1ba3 100644 --- a/Gigya.Microdot.ServiceDiscovery/ConfigDiscoverySource.cs +++ b/Gigya.Microdot.ServiceDiscovery/ConfigDiscoverySource.cs @@ -43,7 +43,7 @@ public class ConfigDiscoverySource : ServiceDiscoverySourceBase private string ConfigPath => $"Discovery.{Deployment}"; - public ConfigDiscoverySource(ServiceDeployment deployment, Func getConfig, ILog log) : base(deployment.ServiceName) + public ConfigDiscoverySource(DeploymentIdentifier deployment, Func getConfig, ILog log) : base(deployment.ServiceName) { _serviceDiscoveryConfig = getConfig().Services[deployment.ServiceName]; Log = log; diff --git a/Gigya.Microdot.ServiceDiscovery/ConsulClient.cs b/Gigya.Microdot.ServiceDiscovery/ConsulClient.cs index faae1b81..36eb8286 100644 --- a/Gigya.Microdot.ServiceDiscovery/ConsulClient.cs +++ b/Gigya.Microdot.ServiceDiscovery/ConsulClient.cs @@ -78,6 +78,8 @@ public class ConsulClient : IConsulClient private bool _disposed; private int _initialized = 0; + private readonly IDisposable _healthCheck; + private Func _getHealthStatus; public ConsulClient(string serviceName, Func getConfig, ISourceBlock configChanged, IEnvironmentVariableProvider environmentVariableProvider, @@ -101,6 +103,7 @@ public ConsulClient(string serviceName, Func getConfig, _resultChanged = new BufferBlock(); _initializedVersion = new TaskCompletionSource(); ShutdownToken = new CancellationTokenSource(); + _healthCheck = _aggregatedHealthStatus.RegisterCheck(_serviceNameOrigin, ()=>_getHealthStatus()); } public Task Init() @@ -197,8 +200,7 @@ private Task ConfigChanged(ConsulConfig c) private async Task LoadServiceVersion() { var config = GetConfig(); - var maxSecondsToWaitForResponse = Math.Max(0, config.HttpTimeout.TotalSeconds - 2); - var urlCommand = $"v1/kv/service/{_serviceName}?dc={DataCenter}&index={_versionModifyIndex}&wait={maxSecondsToWaitForResponse}s"; + var urlCommand = $"v1/kv/service/{_serviceName}?dc={DataCenter}&index={_versionModifyIndex}&wait={config.HttpTimeout.TotalSeconds}s"; var response = await CallConsul(urlCommand, ShutdownToken.Token).ConfigureAwait(false); if (response.ModifyIndex.HasValue) @@ -239,8 +241,7 @@ private Task ReloadServiceVersion() private async Task SearchServiceInAllKeys() { var config = GetConfig(); - var maxSecondsToWaitForResponse = Math.Max(0, config.HttpTimeout.TotalSeconds - 2); - var urlCommand = $"v1/kv/service?dc={DataCenter}&keys&index={_allKeysModifyIndex}&wait={maxSecondsToWaitForResponse}s"; + var urlCommand = $"v1/kv/service?dc={DataCenter}&keys&index={_allKeysModifyIndex}&wait={config.HttpTimeout.TotalSeconds}s"; var response = await CallConsul(urlCommand, ShutdownToken.Token).ConfigureAwait(false); if (response.ModifyIndex.HasValue) @@ -283,8 +284,7 @@ private async Task LoadEndpointsByHealth() return new ConsulResponse { IsDeploymentDefined = false }; var config = GetConfig(); - var maxSecondsToWaitForResponse = Math.Max(0, config.HttpTimeout.TotalSeconds - 2); - var urlCommand = $"v1/health/service/{_serviceName}?dc={DataCenter}&passing&index={_endpointsModifyIndex}&wait={maxSecondsToWaitForResponse}s"; + var urlCommand = $"v1/health/service/{_serviceName}?dc={DataCenter}&passing&index={_endpointsModifyIndex}&wait={config.HttpTimeout.TotalSeconds}s"; var response = await CallConsul(urlCommand, _loadEndpointsByHealthCancellationTokenSource.Token).ConfigureAwait(false); if (response.ModifyIndex.HasValue) @@ -350,7 +350,7 @@ private async Task LoadEndpointsByQuery() } private async Task CallConsul(string urlCommand, CancellationToken cancellationToken) - { + { var timeout = GetConfig().HttpTimeout; ulong? modifyIndex = 0; string requestLog = string.Empty; @@ -359,8 +359,11 @@ private async Task CallConsul(string urlCommand, CancellationTok try { - if (_httpClient == null) - _httpClient = new HttpClient { BaseAddress = ConsulAddress }; + if (_httpClient?.Timeout != timeout) + { + _httpClient?.Dispose(); + _httpClient = new HttpClient {BaseAddress = ConsulAddress, Timeout = timeout}; + } requestLog = _httpClient.BaseAddress + urlCommand; using (var timeoutcancellationToken = new CancellationTokenSource(timeout)) @@ -377,7 +380,7 @@ private async Task CallConsul(string urlCommand, CancellationTok unencrypted: new Tags { {"ConsulAddress", ConsulAddress.ToString()}, - {"ServiceDeployment", _serviceName}, + {"ServiceName", _serviceName}, {"ConsulQuery", urlCommand}, {"ResponseCode", statusCode.ToString()}, {"Content", responseContent} @@ -443,7 +446,7 @@ internal void SetErrorResult(string requestLog, Exception ex, HttpStatusCode? re Content = responseContent }); - _aggregatedHealthStatus.RegisterCheck(_serviceNameOrigin, () => HealthCheckResult.Unhealthy($"{_serviceName} - Consul error: " + ex.Message)); + _getHealthStatus = ()=>HealthCheckResult.Unhealthy($"Consul error: " + ex.Message); if (Result != null && Result.Error == null) return; @@ -477,8 +480,7 @@ internal void SetServiceMissingResult(string requestLog, string responseContent) IsQueryDefined = false }; - _aggregatedHealthStatus.RegisterCheck(_serviceNameOrigin, - () => HealthCheckResult.Healthy($"{_serviceNameOrigin} - Service doesn't exist on Consul")); + _getHealthStatus = ()=>HealthCheckResult.Healthy($"Service doesn't exist on Consul"); } } @@ -513,13 +515,13 @@ private void SetResult(ServiceEntry[] nodes, string requestLog, string responseC } - _aggregatedHealthStatus.RegisterCheck(_serviceNameOrigin, () => + _getHealthStatus = () => { if (_serviceName == _serviceNameOrigin) - return HealthCheckResult.Healthy($"{_serviceNameOrigin} - {healthMessage}"); + return HealthCheckResult.Healthy(healthMessage); else - return HealthCheckResult.Healthy($"{_serviceNameOrigin} - Service exists on Consul, but with different casing: '{_serviceName}'. {healthMessage}"); - }); + return HealthCheckResult.Healthy($"Service exists on Consul, but with different casing: '{_serviceName}'. {healthMessage}"); + }; Result = new EndPointsResult { @@ -552,7 +554,7 @@ public void Dispose() _loadEndpointsByHealthCancellationTokenSource?.Cancel(); _waitForConfigChange.TrySetCanceled(); _initializedVersion.TrySetCanceled(); - _aggregatedHealthStatus.RemoveCheck(_serviceNameOrigin); + _healthCheck.Dispose(); } @@ -570,117 +572,4 @@ private class ConsulResponse } } - public class ConsulQueryExecuteResponse - { - public string Service { get; set; } - - public ServiceEntry[] Nodes { get; set; } - - public QueryDNSOptions DNS { get; set; } - - public string Datacenter { get; set; } - - public int Failovers { get; set; } - } - - public class ServiceEntry - { - public Node Node { get; set; } - - public AgentService Service { get; set; } - - public HealthCheck[] Checks { get; set; } - } - - public class Node - { - [JsonProperty(PropertyName = "Node")] - public string Name { get; set; } - - public string Address { get; set; } - - public ulong ModifyIndex { get; set; } - - public Dictionary TaggedAddresses { get; set; } - } - - public class AgentService - { - public string ID { get; set; } - - public string Service { get; set; } - - public string[] Tags { get; set; } - - public int Port { get; set; } - - public string Address { get; set; } - - public bool EnableTagOverride { get; set; } - } - - public class HealthCheck - { - public string Node { get; set; } - - public string CheckID { get; set; } - - public string Name { get; set; } - - public string Status { get; set; } - - public string Notes { get; set; } - - public string Output { get; set; } - - public string ServiceID { get; set; } - - public string ServiceName { get; set; } - } - - public class QueryDNSOptions - { - public string TTL { get; set; } - } - - public class KeyValueResponse - { - public int LockIndex { get; set; } - public string Key { get; set; } - public int Flags { get; set; } - public string Value { get; set; } - public ulong CreateIndex { get; set; } - public ulong ModifyIndex { get; set; } - - public ServiceKeyValue TryDecodeValue() - { - if (Value == null) - return null; - - try - { - var serialized = Encoding.UTF8.GetString(Convert.FromBase64String(Value)); - return JsonConvert.DeserializeObject(serialized); - } - catch - { - return null; - } - } - } - - public class ServiceKeyValue - { - [JsonProperty("basePort")] - public int BasePort { get; set; } - - [JsonProperty("dc")] - public string DataCenter { get; set; } - - [JsonProperty("env")] - public string Environment { get; set; } - - [JsonProperty("version")] - public string Version { get; set; } - } } \ No newline at end of file diff --git a/Gigya.Microdot.ServiceDiscovery/ConsulContracts.cs b/Gigya.Microdot.ServiceDiscovery/ConsulContracts.cs new file mode 100644 index 00000000..1470bb1c --- /dev/null +++ b/Gigya.Microdot.ServiceDiscovery/ConsulContracts.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Gigya.Microdot.ServiceDiscovery.Rewrite; +using Newtonsoft.Json; + +namespace Gigya.Microdot.ServiceDiscovery +{ + public class ConsulQueryExecuteResponse + { + public ServiceEntry[] Nodes { get; set; } + } + + public class ServiceEntry + { + public NodeEntry Node { get; set; } + + public AgentService Service { get; set; } + + } + + public class NodeEntry + { + [JsonProperty(PropertyName = "Node")] + public string Name { get; set; } + } + + public class AgentService + { + public string[] Tags { get; set; } + + public int Port { get; set; } + + } + + + public class KeyValueResponse + { + public string Value { get; set; } + + public ServiceKeyValue TryDecodeValue() + { + if (Value == null) + return null; + + try + { + var serialized = Encoding.UTF8.GetString(Convert.FromBase64String(Value)); + return JsonConvert.DeserializeObject(serialized); + } + catch + { + return null; + } + } + } + + public class ServiceKeyValue + { + [JsonProperty("basePort")] + public int BasePort { get; set; } + + [JsonProperty("dc")] + public string DataCenter { get; set; } + + [JsonProperty("env")] + public string Environment { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } + } +} diff --git a/Gigya.Microdot.ServiceDiscovery/ConsulDiscoverySource.cs b/Gigya.Microdot.ServiceDiscovery/ConsulDiscoverySource.cs index 7b1d3c37..6ecf1117 100644 --- a/Gigya.Microdot.ServiceDiscovery/ConsulDiscoverySource.cs +++ b/Gigya.Microdot.ServiceDiscovery/ConsulDiscoverySource.cs @@ -61,11 +61,11 @@ public class ConsulDiscoverySource : ServiceDiscoverySourceBase private bool _disposed; - public ConsulDiscoverySource(ServiceDeployment serviceDeployment, + public ConsulDiscoverySource(DeploymentIdentifier deploymentIdentifier, IDateTime dateTime, Func getConfig, Func getConsulClient, ILog log) - : base(GetDeploymentName(serviceDeployment, getConfig().Services[serviceDeployment.ServiceName])) + : base(GetDeploymentName(deploymentIdentifier, getConfig().Services[deploymentIdentifier.ServiceName])) { DateTime = dateTime; @@ -194,13 +194,13 @@ public override void ShutDown() _disposed = true; } - public static string GetDeploymentName(ServiceDeployment serviceDeployment, ServiceDiscoveryConfig serviceDiscoverySettings) + public static string GetDeploymentName(DeploymentIdentifier deploymentIdentifier, ServiceDiscoveryConfig serviceDiscoverySettings) { if (serviceDiscoverySettings.Scope == ServiceScope.DataCenter) { - return serviceDeployment.ServiceName; + return deploymentIdentifier.ServiceName; } - return $"{serviceDeployment.ServiceName}-{serviceDeployment.DeploymentEnvironment}"; + return deploymentIdentifier.ToString(); } } diff --git a/Gigya.Microdot.ServiceDiscovery/ServiceDeployment.cs b/Gigya.Microdot.ServiceDiscovery/DeploymentIdentifier.cs similarity index 60% rename from Gigya.Microdot.ServiceDiscovery/ServiceDeployment.cs rename to Gigya.Microdot.ServiceDiscovery/DeploymentIdentifier.cs index 4f6da804..573f1ad3 100644 --- a/Gigya.Microdot.ServiceDiscovery/ServiceDeployment.cs +++ b/Gigya.Microdot.ServiceDiscovery/DeploymentIdentifier.cs @@ -21,22 +21,28 @@ #endregion namespace Gigya.Microdot.ServiceDiscovery { - public class ServiceDeployment + public class DeploymentIdentifier { public string DeploymentEnvironment { get; } public string ServiceName { get; } + public bool IsEnvironmentSpecific => string.IsNullOrEmpty(DeploymentEnvironment)==false; - public ServiceDeployment(string serviceName, string deploymentEnvironment) + public DeploymentIdentifier(string serviceName, string deploymentEnvironment) { - DeploymentEnvironment = deploymentEnvironment.ToLower(); + DeploymentEnvironment = deploymentEnvironment?.ToLower(); ServiceName = serviceName; } + public DeploymentIdentifier(string serviceName): this(serviceName, null) + { } public override string ToString() { - return $"{ServiceName}-{DeploymentEnvironment}"; + if (IsEnvironmentSpecific) + return $"{ServiceName}-{DeploymentEnvironment}"; + else + return ServiceName; } @@ -48,12 +54,15 @@ public override bool Equals(object obj) if (ReferenceEquals(this, obj)) return true; - ServiceDeployment other = obj as ServiceDeployment; - - if (other == null) + if (obj is DeploymentIdentifier other) + { + if (IsEnvironmentSpecific && other.IsEnvironmentSpecific) + return DeploymentEnvironment == other.DeploymentEnvironment && ServiceName == other.ServiceName; + else + return ServiceName == other.ServiceName; + } + else return false; - - return DeploymentEnvironment == other.DeploymentEnvironment && ServiceName == other.ServiceName; } @@ -61,7 +70,10 @@ public override int GetHashCode() { unchecked { - return ((DeploymentEnvironment?.GetHashCode() ?? 0) * 397) ^ (ServiceName?.GetHashCode() ?? 0); + if (IsEnvironmentSpecific) + return ((ServiceName?.GetHashCode() ?? 0) * 397) ^ (DeploymentEnvironment.GetHashCode()); + else + return ServiceName?.GetHashCode() ?? 0; } } } diff --git a/Gigya.Microdot.ServiceDiscovery/DiscoverySourceLoader.cs b/Gigya.Microdot.ServiceDiscovery/DiscoverySourceLoader.cs index bf46bb92..f67dd3e6 100644 --- a/Gigya.Microdot.ServiceDiscovery/DiscoverySourceLoader.cs +++ b/Gigya.Microdot.ServiceDiscovery/DiscoverySourceLoader.cs @@ -29,16 +29,16 @@ namespace Gigya.Microdot.ServiceDiscovery { public class DiscoverySourceLoader : IDiscoverySourceLoader { - private readonly Func _getSources; + private readonly Func _getSources; - public DiscoverySourceLoader(Func getSources) + public DiscoverySourceLoader(Func getSources) { _getSources = getSources; } - public IServiceDiscoverySource GetDiscoverySource(ServiceDeployment serviceDeployment, ServiceDiscoveryConfig serviceDiscoveryConfig) + public IServiceDiscoverySource GetDiscoverySource(DeploymentIdentifier deploymentIdentifier, ServiceDiscoveryConfig serviceDiscoveryConfig) { - var source = _getSources(serviceDeployment).FirstOrDefault(f=>f.SourceName.Equals(serviceDiscoveryConfig.Source, StringComparison.InvariantCultureIgnoreCase)); + var source = _getSources(deploymentIdentifier).FirstOrDefault(f=>f.SourceName.Equals(serviceDiscoveryConfig.Source, StringComparison.InvariantCultureIgnoreCase)); if (source==null) throw new ConfigurationException($"Discovery Source '{serviceDiscoveryConfig.Source}' is not supported."); @@ -49,6 +49,6 @@ public IServiceDiscoverySource GetDiscoverySource(ServiceDeployment serviceDeplo public interface IDiscoverySourceLoader { - IServiceDiscoverySource GetDiscoverySource(ServiceDeployment serviceDeployment, ServiceDiscoveryConfig serviceDiscoveryConfig); + IServiceDiscoverySource GetDiscoverySource(DeploymentIdentifier deploymentIdentifier, ServiceDiscoveryConfig serviceDiscoveryConfig); } } \ No newline at end of file diff --git a/Gigya.Microdot.ServiceDiscovery/Gigya.Microdot.ServiceDiscovery.csproj b/Gigya.Microdot.ServiceDiscovery/Gigya.Microdot.ServiceDiscovery.csproj index 08e30ce7..30a8cdb6 100644 --- a/Gigya.Microdot.ServiceDiscovery/Gigya.Microdot.ServiceDiscovery.csproj +++ b/Gigya.Microdot.ServiceDiscovery/Gigya.Microdot.ServiceDiscovery.csproj @@ -21,6 +21,7 @@ prompt 4 bin\Debug\Gigya.Microdot.ServiceDiscovery.xml + MinimumRecommendedRules.ruleset pdbonly @@ -56,20 +57,40 @@ + + + + + + + + - + - + + + + + + + + + + + + + @@ -81,6 +102,10 @@ + + {0E3A2422-DD99-4D75-A18C-96329A842742} + Gigya.Microdot.Configuration + {A90D7C71-EC7C-4328-9DB1-D2C3A30727DB} Gigya.Microdot.Interfaces @@ -90,6 +115,10 @@ Gigya.Microdot.SharedLogic + + + +