Feat/monorepo (#734)

* copied everything from repos back to ocelot repo

* added src projects to sln

* removed all test projects that have no tests

* added all test projects to sln

* removed test not on master

* merged unit tests

* merged acceptance tests

* merged integration tests

* fixed namepaces

* build script creates packages for all projects

* updated docs to make sure no references to external repos that we will remove

* +semver: breaking
This commit is contained in:
Tom Pallister
2019-01-07 19:52:53 +00:00
committed by GitHub
parent 35253025c7
commit 11a2d13f18
107 changed files with 9483 additions and 585 deletions

View File

@ -0,0 +1,76 @@
namespace Ocelot.Provider.Consul
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using global::Consul;
using Infrastructure.Extensions;
using Logging;
using ServiceDiscovery.Providers;
using Values;
public class Consul : IServiceDiscoveryProvider
{
private readonly ConsulRegistryConfiguration _config;
private readonly IOcelotLogger _logger;
private readonly IConsulClient _consul;
private const string VersionPrefix = "version-";
public Consul(ConsulRegistryConfiguration config, IOcelotLoggerFactory factory, IConsulClientFactory clientFactory)
{
_logger = factory.CreateLogger<Consul>();
_config = config;
_consul = clientFactory.Get(_config);
}
public async Task<List<Service>> Get()
{
var queryResult = await _consul.Health.Service(_config.KeyOfServiceInConsul, string.Empty, true);
var services = new List<Service>();
foreach (var serviceEntry in queryResult.Response)
{
if (IsValid(serviceEntry))
{
services.Add(BuildService(serviceEntry));
}
else
{
_logger.LogWarning($"Unable to use service Address: {serviceEntry.Service.Address} and Port: {serviceEntry.Service.Port} as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
}
}
return services.ToList();
}
private Service BuildService(ServiceEntry serviceEntry)
{
return new Service(
serviceEntry.Service.Service,
new ServiceHostAndPort(serviceEntry.Service.Address, serviceEntry.Service.Port),
serviceEntry.Service.ID,
GetVersionFromStrings(serviceEntry.Service.Tags),
serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
}
private bool IsValid(ServiceEntry serviceEntry)
{
if (string.IsNullOrEmpty(serviceEntry.Service.Address) || serviceEntry.Service.Address.Contains("http://") || serviceEntry.Service.Address.Contains("https://") || serviceEntry.Service.Port <= 0)
{
return false;
}
return true;
}
private string GetVersionFromStrings(IEnumerable<string> strings)
{
return strings
?.FirstOrDefault(x => x.StartsWith(VersionPrefix, StringComparison.Ordinal))
.TrimStart(VersionPrefix);
}
}
}

View File

@ -0,0 +1,21 @@
namespace Ocelot.Provider.Consul
{
using System;
using global::Consul;
public class ConsulClientFactory : IConsulClientFactory
{
public IConsulClient Get(ConsulRegistryConfiguration config)
{
return new ConsulClient(c =>
{
c.Address = new Uri($"http://{config.Host}:{config.Port}");
if (!string.IsNullOrEmpty(config?.Token))
{
c.Token = config.Token;
}
});
}
}
}

View File

@ -0,0 +1,96 @@
namespace Ocelot.Provider.Consul
{
using System;
using System.Text;
using System.Threading.Tasks;
using Configuration.File;
using Configuration.Repository;
using global::Consul;
using Logging;
using Newtonsoft.Json;
using Responses;
public class ConsulFileConfigurationRepository : IFileConfigurationRepository
{
private readonly IConsulClient _consul;
private readonly string _configurationKey;
private readonly Cache.IOcelotCache<FileConfiguration> _cache;
private readonly IOcelotLogger _logger;
public ConsulFileConfigurationRepository(
Cache.IOcelotCache<FileConfiguration> cache,
IInternalConfigurationRepository repo,
IConsulClientFactory factory,
IOcelotLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<ConsulFileConfigurationRepository>();
_cache = cache;
var internalConfig = repo.Get();
_configurationKey = "InternalConfiguration";
string token = null;
if (!internalConfig.IsError)
{
token = internalConfig.Data.ServiceProviderConfiguration.Token;
_configurationKey = !string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey) ?
internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey : _configurationKey;
}
var config = new ConsulRegistryConfiguration(internalConfig.Data.ServiceProviderConfiguration.Host,
internalConfig.Data.ServiceProviderConfiguration.Port, _configurationKey, token);
_consul = factory.Get(config);
}
public async Task<Response<FileConfiguration>> Get()
{
var config = _cache.Get(_configurationKey, _configurationKey);
if (config != null)
{
return new OkResponse<FileConfiguration>(config);
}
var queryResult = await _consul.KV.Get(_configurationKey);
if (queryResult.Response == null)
{
return new OkResponse<FileConfiguration>(null);
}
var bytes = queryResult.Response.Value;
var json = Encoding.UTF8.GetString(bytes);
var consulConfig = JsonConvert.DeserializeObject<FileConfiguration>(json);
return new OkResponse<FileConfiguration>(consulConfig);
}
public async Task<Response> Set(FileConfiguration ocelotConfiguration)
{
var json = JsonConvert.SerializeObject(ocelotConfiguration, Formatting.Indented);
var bytes = Encoding.UTF8.GetBytes(json);
var kvPair = new KVPair(_configurationKey)
{
Value = bytes
};
var result = await _consul.KV.Put(kvPair);
if (result.Response)
{
_cache.AddAndDelete(_configurationKey, ocelotConfiguration, TimeSpan.FromSeconds(3), _configurationKey);
return new OkResponse();
}
return new ErrorResponse(new UnableToSetConfigInConsulError($"Unable to set FileConfiguration in consul, response status code from consul was {result.StatusCode}"));
}
}
}

View File

@ -0,0 +1,93 @@
namespace Ocelot.Provider.Consul
{
using System;
using System.Linq;
using System.Threading.Tasks;
using Configuration.Creator;
using Configuration.File;
using Configuration.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Middleware;
using Responses;
public static class ConsulMiddlewareConfigurationProvider
{
public static OcelotMiddlewareConfigurationDelegate Get = async builder =>
{
var fileConfigRepo = builder.ApplicationServices.GetService<IFileConfigurationRepository>();
var fileConfig = builder.ApplicationServices.GetService<IOptionsMonitor<FileConfiguration>>();
var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>();
var internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();
if (UsingConsul(fileConfigRepo))
{
await SetFileConfigInConsul(builder, fileConfigRepo, fileConfig, internalConfigCreator, internalConfigRepo);
}
};
private static bool UsingConsul(IFileConfigurationRepository fileConfigRepo)
{
return fileConfigRepo.GetType() == typeof(ConsulFileConfigurationRepository);
}
private static async Task SetFileConfigInConsul(IApplicationBuilder builder,
IFileConfigurationRepository fileConfigRepo, IOptionsMonitor<FileConfiguration> fileConfig,
IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo)
{
// get the config from consul.
var fileConfigFromConsul = await fileConfigRepo.Get();
if (IsError(fileConfigFromConsul))
{
ThrowToStopOcelotStarting(fileConfigFromConsul);
}
else if (ConfigNotStoredInConsul(fileConfigFromConsul))
{
//there was no config in consul set the file in config in consul
await fileConfigRepo.Set(fileConfig.CurrentValue);
}
else
{
// create the internal config from consul data
var internalConfig = await internalConfigCreator.Create(fileConfigFromConsul.Data);
if (IsError(internalConfig))
{
ThrowToStopOcelotStarting(internalConfig);
}
else
{
// add the internal config to the internal repo
var response = internalConfigRepo.AddOrReplace(internalConfig.Data);
if (IsError(response))
{
ThrowToStopOcelotStarting(response);
}
}
if (IsError(internalConfig))
{
ThrowToStopOcelotStarting(internalConfig);
}
}
}
private static void ThrowToStopOcelotStarting(Response config)
{
throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}");
}
private static bool IsError(Response response)
{
return response == null || response.IsError;
}
private static bool ConfigNotStoredInConsul(Response<FileConfiguration> fileConfigFromConsul)
{
return fileConfigFromConsul.Data == null;
}
}
}

View File

@ -0,0 +1,29 @@
namespace Ocelot.Provider.Consul
{
using System.Threading.Tasks;
using Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using ServiceDiscovery;
public static class ConsulProviderFactory
{
public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) =>
{
var factory = provider.GetService<IOcelotLoggerFactory>();
var consulFactory = provider.GetService<IConsulClientFactory>();
var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Host, config.Port, name, config.Token);
var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory);
if (config.Type?.ToLower() == "pollconsul")
{
return new PollConsul(config.PollingInterval, factory, consulServiceDiscoveryProvider);
}
return consulServiceDiscoveryProvider;
};
}
}

View File

@ -0,0 +1,18 @@
namespace Ocelot.Provider.Consul
{
public class ConsulRegistryConfiguration
{
public ConsulRegistryConfiguration(string host, int port, string keyOfServiceInConsul, string token)
{
Host = string.IsNullOrEmpty(host) ? "localhost" : host;
Port = port > 0 ? port : 8500;
KeyOfServiceInConsul = keyOfServiceInConsul;
Token = token;
}
public string KeyOfServiceInConsul { get; }
public string Host { get; }
public int Port { get; }
public string Token { get; }
}
}

View File

@ -0,0 +1,9 @@
namespace Ocelot.Provider.Consul
{
using global::Consul;
public interface IConsulClientFactory
{
IConsulClient Get(ConsulRegistryConfiguration config);
}
}

View File

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<NETStandardImplicitPackageVersion>2.0.0</NETStandardImplicitPackageVersion>
<NoPackageAnalysis>true</NoPackageAnalysis>
<Description>Provides Ocelot extensions to use Consul</Description>
<AssemblyTitle>Ocelot.Provider.Consul</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot.Provider.Consul</AssemblyName>
<PackageId>Ocelot.Provider.Consul</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
<PackageProjectUrl>https://github.com/ThreeMammals/Ocelot.Provider.Consul</PackageProjectUrl>
<PackageIconUrl>http://threemammals.com/images/ocelot_logo.png</PackageIconUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Consul" Version="0.7.2.6" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,26 @@
namespace Ocelot.Provider.Consul
{
using Configuration.Repository;
using DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Middleware;
using ServiceDiscovery;
public static class OcelotBuilderExtensions
{
public static IOcelotBuilder AddConsul(this IOcelotBuilder builder)
{
builder.Services.AddSingleton<ServiceDiscoveryFinderDelegate>(ConsulProviderFactory.Get);
builder.Services.AddSingleton<IConsulClientFactory, ConsulClientFactory>();
return builder;
}
public static IOcelotBuilder AddConfigStoredInConsul(this IOcelotBuilder builder)
{
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(ConsulMiddlewareConfigurationProvider.Get);
builder.Services.AddHostedService<FileConfigurationPoller>();
builder.Services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
return builder;
}
}
}

View File

@ -0,0 +1,47 @@
namespace Ocelot.Provider.Consul
{
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Logging;
using ServiceDiscovery.Providers;
using Values;
public class PollConsul : IServiceDiscoveryProvider
{
private readonly IOcelotLogger _logger;
private readonly IServiceDiscoveryProvider _consulServiceDiscoveryProvider;
private readonly Timer _timer;
private bool _polling;
private List<Service> _services;
public PollConsul(int pollingInterval, IOcelotLoggerFactory factory, IServiceDiscoveryProvider consulServiceDiscoveryProvider)
{
_logger = factory.CreateLogger<PollConsul>();
_consulServiceDiscoveryProvider = consulServiceDiscoveryProvider;
_services = new List<Service>();
_timer = new Timer(async x =>
{
if (_polling)
{
return;
}
_polling = true;
await Poll();
_polling = false;
}, null, pollingInterval, pollingInterval);
}
public Task<List<Service>> Get()
{
return Task.FromResult(_services);
}
private async Task Poll()
{
_services = await _consulServiceDiscoveryProvider.Get();
}
}
}

View File

@ -0,0 +1,18 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Ocelot")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("d6df4206-0dba-41d8-884d-c3e08290fdbb")]

View File

@ -0,0 +1,12 @@
namespace Ocelot.Provider.Consul
{
using Errors;
public class UnableToSetConfigInConsulError : Error
{
public UnableToSetConfigInConsulError(string s)
: base(s, OcelotErrorCode.UnknownError)
{
}
}
}