-
Notifications
You must be signed in to change notification settings - Fork 8
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
aad905a
commit 95d3f4b
Showing
14 changed files
with
505 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net5.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Consul" Version="1.6.1.1" /> | ||
<PackageReference Include="Microsoft.ReverseProxy" Version="1.0.0-preview.4.20424.2" /> | ||
<PackageReference Include="System.Interactive" Version="4.1.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
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,111 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Consul; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.ReverseProxy.Abstractions; | ||
using Microsoft.ReverseProxy.Service; | ||
|
||
namespace CCProxy | ||
{ | ||
public class ConsulMonitorWorker : BackgroundService | ||
{ | ||
private readonly IConsulClient _consulClient; | ||
private readonly IProxyConfigProvider _proxyConfigProvider; | ||
private readonly IConfigValidator _proxyConfigValidator; | ||
private readonly ILogger<ConsulMonitorWorker> _logger; | ||
private const int DEFAULT_CONSUL_POLL_INTERVAL_MINS = 2; | ||
|
||
public ConsulMonitorWorker(IConsulClient consulClient, IProxyConfigProvider proxyConfigProvider, | ||
IConfigValidator proxyConfigValidator, ILogger<ConsulMonitorWorker> logger) | ||
{ | ||
_consulClient = consulClient; | ||
_proxyConfigProvider = proxyConfigProvider; | ||
_proxyConfigValidator = proxyConfigValidator; | ||
_logger = logger; | ||
} | ||
|
||
protected override async Task ExecuteAsync(CancellationToken stoppingToken) | ||
{ | ||
while (!stoppingToken.IsCancellationRequested) | ||
{ | ||
var serviceResult = await _consulClient.Agent.Services(stoppingToken); | ||
|
||
if (serviceResult.StatusCode == HttpStatusCode.OK) | ||
{ | ||
var clusters = await ExtractClusters(serviceResult); | ||
var routes = await ExtractRoutes(serviceResult); | ||
|
||
(_proxyConfigProvider as ConsulProxyConfigProvider)?.Update(routes, clusters); | ||
} | ||
|
||
await Task.Delay(TimeSpan.FromMinutes(DEFAULT_CONSUL_POLL_INTERVAL_MINS), stoppingToken); | ||
} | ||
} | ||
|
||
private async Task<List<Cluster>> ExtractClusters(QueryResult<Dictionary<string, AgentService>> serviceResult) | ||
{ | ||
var clusters = new Dictionary<string, Cluster>(); | ||
var serviceMapping = serviceResult.Response; | ||
foreach (var (key, svc) in serviceMapping) | ||
{ | ||
var cluster = clusters.ContainsKey(svc.Service) | ||
? clusters[svc.Service] | ||
: new Cluster {Id = svc.Service}; | ||
|
||
cluster.Destinations.Add(svc.ID, new Destination {Address = $"{svc.Address}:{svc.Port}"}); | ||
|
||
var clusterErrs = await _proxyConfigValidator.ValidateClusterAsync(cluster); | ||
if (clusterErrs.Any()) | ||
{ | ||
_logger.LogError("Errors found when creating clusters for {Service}", svc.Service); | ||
clusterErrs.ForEach(err => _logger.LogError(err, $"{svc.Service} cluster validation error")); | ||
continue; | ||
} | ||
|
||
clusters[svc.Service] = cluster; | ||
} | ||
|
||
return clusters.Values.ToList(); | ||
} | ||
|
||
private async Task<List<ProxyRoute>> ExtractRoutes(QueryResult<Dictionary<string, AgentService>> serviceResult) | ||
{ | ||
var serviceMapping = serviceResult.Response; | ||
List<ProxyRoute> routes = new List<ProxyRoute>(); | ||
foreach (var (key, svc) in serviceMapping) | ||
{ | ||
if (svc.Meta.TryGetValue("yarp", out string enableYarp) && | ||
enableYarp.Equals("on", StringComparison.InvariantCulture)) | ||
{ | ||
if (routes.Any(r => r.ClusterId == svc.Service)) continue; | ||
|
||
ProxyRoute route = new ProxyRoute | ||
{ | ||
ClusterId = svc.Service, | ||
RouteId = $"{svc.Service}-route", | ||
Match = | ||
{ | ||
Path = svc.Meta.ContainsKey("yarp_path")?svc.Meta["yarp_path"] : default, | ||
Hosts = svc.Meta.ContainsKey("yarp_hosts")? svc.Meta["yarp_hosts"].Split(',') : default | ||
} | ||
}; | ||
|
||
var routeErrs = await _proxyConfigValidator.ValidateRouteAsync(route); | ||
if (routeErrs.Any()) | ||
{ | ||
_logger.LogError("Errors found when trying to generate routes for {Service}", svc.Service); | ||
routeErrs.ForEach(err => _logger.LogError(err, $"{svc.Service} route validation error")); | ||
continue; | ||
} | ||
routes.Add(route); | ||
} | ||
} | ||
return routes; | ||
} | ||
} | ||
} |
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,50 @@ | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using Consul; | ||
using Microsoft.Extensions.Primitives; | ||
using Microsoft.ReverseProxy.Abstractions; | ||
using Microsoft.ReverseProxy.Service; | ||
|
||
namespace CCProxy | ||
{ | ||
public class ConsulProxyConfigProvider : IProxyConfigProvider | ||
{ | ||
private readonly IConsulClient _consulClient; | ||
private volatile ConsulProxyConfig _config; | ||
|
||
public ConsulProxyConfigProvider(IConsulClient consulClient) | ||
{ | ||
_consulClient = consulClient; | ||
_config = new ConsulProxyConfig(null, null); | ||
} | ||
|
||
public IProxyConfig GetConfig() => _config; | ||
|
||
public virtual void Update(IReadOnlyList<ProxyRoute> routes, IReadOnlyList<Cluster> clusters) | ||
{ | ||
var oldConfig = _config; | ||
_config = new ConsulProxyConfig(routes, clusters); | ||
oldConfig.SignalChange(); | ||
} | ||
|
||
private class ConsulProxyConfig : IProxyConfig | ||
{ | ||
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); | ||
public IReadOnlyList<ProxyRoute> Routes { get; } | ||
public IReadOnlyList<Cluster> Clusters { get; } | ||
public IChangeToken ChangeToken { get; } | ||
|
||
public ConsulProxyConfig(IReadOnlyList<ProxyRoute> routes, IReadOnlyList<Cluster> clusters) | ||
{ | ||
Routes = routes; | ||
Clusters = clusters; | ||
ChangeToken = new CancellationChangeToken(_cts.Token); | ||
} | ||
|
||
internal void SignalChange() | ||
{ | ||
_cts.Cancel(); | ||
} | ||
} | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
src/CodeConfigSample/CCProxy/Controllers/YarpController.cs
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,52 @@ | ||
using System; | ||
using System.Linq; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.ReverseProxy.Service; | ||
|
||
namespace CCProxy.Controllers | ||
{ | ||
[Route("/yarp")] | ||
[ApiController] | ||
public class YarpController : Controller | ||
{ | ||
private readonly IProxyConfigProvider _proxyConfigProvider; | ||
|
||
public YarpController(IProxyConfigProvider proxyConfigProvider) | ||
{ | ||
_proxyConfigProvider = proxyConfigProvider; | ||
} | ||
|
||
[HttpGet("routes")] | ||
public ActionResult DumpRoutes() | ||
{ | ||
var proxyConfig = _proxyConfigProvider.GetConfig(); | ||
return Ok(proxyConfig.Routes); | ||
} | ||
|
||
[HttpGet("clusters")] | ||
public ActionResult DumpClusters() | ||
{ | ||
var proxyConfig = _proxyConfigProvider.GetConfig(); | ||
return Ok(proxyConfig.Clusters); | ||
} | ||
|
||
[HttpGet("incoming")] | ||
public IActionResult Dump() | ||
{ | ||
var result = new | ||
{ | ||
Request.Method, | ||
Request.Protocol, | ||
Request.Scheme, | ||
Host = Request.Host.Value, | ||
PathBase = Request.PathBase.Value, | ||
Path = Request.Path.Value, | ||
Query = Request.QueryString.Value, | ||
Headers = Request.Headers.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray()), | ||
Time = DateTimeOffset.UtcNow | ||
}; | ||
|
||
return Ok(result); | ||
} | ||
} | ||
} |
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,19 @@ | ||
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base | ||
WORKDIR /app | ||
EXPOSE 80 | ||
|
||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build | ||
WORKDIR /src | ||
COPY ["CCProxy.csproj", "./"] | ||
RUN dotnet restore "CCProxy.csproj" | ||
COPY . . | ||
WORKDIR "/src" | ||
RUN dotnet build "CCProxy.csproj" -c Release -o /app/build | ||
|
||
FROM build AS publish | ||
RUN dotnet publish "CCProxy.csproj" -c Release -o /app/publish | ||
|
||
FROM base AS final | ||
WORKDIR /app | ||
COPY --from=publish /app/publish . | ||
ENTRYPOINT ["dotnet", "CCProxy.dll"] |
57 changes: 57 additions & 0 deletions
57
src/CodeConfigSample/CCProxy/Middleware/HttpInspectorMiddleware.cs
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,57 @@ | ||
using System; | ||
using System.Data.Common; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.ReverseProxy.Middleware; | ||
|
||
namespace CCProxy.Middleware | ||
{ | ||
public class HttpInspectorMiddleware | ||
{ | ||
private readonly RequestDelegate _next; | ||
private readonly ILogger _logger; | ||
|
||
public HttpInspectorMiddleware(RequestDelegate next, | ||
ILogger<HttpInspectorMiddleware> logger) | ||
{ | ||
_next = next; | ||
_logger = logger; | ||
} | ||
|
||
public async Task Invoke(HttpContext context) | ||
{ | ||
LogRequest(context); | ||
LogDestinations(context); | ||
await _next(context); | ||
} | ||
|
||
private void LogDestinations(HttpContext context) | ||
{ | ||
_logger.LogDebug("Available Destinations: "); | ||
var proxyFeature = context.Features.Get<IReverseProxyFeature>(); | ||
|
||
proxyFeature.AvailableDestinations.ForEach(dest => | ||
{ | ||
_logger.LogDebug("Destination {ID} {Address}", dest.DestinationId, dest.Config.Address); | ||
}); | ||
} | ||
|
||
private void LogRequest(HttpContext context) | ||
{ | ||
var buffer = new StringBuilder(); | ||
|
||
context.Request.Headers | ||
.ForEach(kvp => buffer.Append($"{kvp.Key}: {kvp.Value}{Environment.NewLine}")); | ||
|
||
_logger.LogDebug($"Http Request Information:{Environment.NewLine}" + | ||
$"Schema: {context.Request.Scheme}{Environment.NewLine}" + | ||
$"Host: {context.Request.Host}{Environment.NewLine}" + | ||
$"Path: {context.Request.Path}{Environment.NewLine}" + | ||
$"QueryString: {context.Request.QueryString}{Environment.NewLine}" + | ||
$"Headers: {Environment.NewLine}{buffer}{Environment.NewLine}"); | ||
} | ||
} | ||
} |
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,26 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace CCProxy | ||
{ | ||
public class Program | ||
{ | ||
public static void Main(string[] args) | ||
{ | ||
CreateHostBuilder(args).Build().Run(); | ||
} | ||
|
||
public static IHostBuilder CreateHostBuilder(string[] args) => | ||
Host.CreateDefaultBuilder(args) | ||
.ConfigureWebHostDefaults(webBuilder => | ||
{ | ||
webBuilder.UseStartup<Startup>(); | ||
}); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/CodeConfigSample/CCProxy/Properties/launchSettings.json
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,14 @@ | ||
{ | ||
"$schema": "http://json.schemastore.org/launchsettings.json", | ||
"profiles": { | ||
"CCProxy": { | ||
"commandName": "Project", | ||
"dotnetRunMessages": "true", | ||
"applicationUrl": "http://localhost:5000", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development", | ||
"ASPNETCORE_URLS" : "http://0.0.0.0:5000" | ||
} | ||
} | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/CodeConfigSample/CCProxy/ServiceCollectionExtensions.cs
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,31 @@ | ||
using System; | ||
using Consul; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
|
||
namespace CCProxy | ||
{ | ||
public static class ServiceCollectionExtensions | ||
{ | ||
public static IServiceCollection AddConsulClient(this IServiceCollection services) | ||
{ | ||
return services.AddConsulClient(options => { }); | ||
} | ||
|
||
public static IServiceCollection AddConsulClient( | ||
this IServiceCollection services, | ||
Action<ConsulClientConfiguration> options) | ||
{ | ||
/* | ||
* CONSUL_HTTP_ADDR | ||
* CONSUL_HTTP_SSL | ||
* CONSUL_HTTP_SSL_VERIFY | ||
* CONSUL_HTTP_AUTH | ||
* CONSUL_HTTP_TOKEN | ||
*/ | ||
services.TryAddSingleton<IConsulClient>(sp => new ConsulClient(options)); | ||
|
||
return services; | ||
} | ||
} | ||
} |
Oops, something went wrong.