Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the config object model read-only #662

Merged
26 commits merged into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageIcon></PackageIcon>
<PackageIconFullPath></PackageIconFullPath>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<PropertyGroup>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand All @@ -16,4 +17,4 @@
<PropertyGroup>
<MoqPublicKey>0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7</MoqPublicKey>
</PropertyGroup>
</Project>
</Project>
4 changes: 2 additions & 2 deletions docs/docfx/articles/configproviders.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void ConfigureServices(IServiceCollection services)
{
RouteId = "route1",
ClusterId = "cluster1",
Match =
Match = new ProxyMatch
{
Path = "{**catch-all}"
}
Expand All @@ -135,7 +135,7 @@ public void ConfigureServices(IServiceCollection services)
new Cluster()
{
Id = "cluster1",
Destinations =
Destinations = new Dictionary<string, Destination>(StringComparer.OrdinalIgnoreCase)
{
{ "destination1", new Destination() { Address = "https://example.com" } }
}
Expand Down
2 changes: 1 addition & 1 deletion docs/docfx/articles/direct-proxying.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void Configure(IApplicationBuilder app, IHttpProxy httpProxy)
UseCookies = false
});
var transformer = new CustomTransformer(); // or HttpTransformer.Default;
var requestOptions = new RequestProxyOptions(TimeSpan.FromSeconds(100), null);
var requestOptions = new RequestProxyOptions { Timeout = TimeSpan.FromSeconds(100) };

app.UseRouting();
app.UseAuthorization();
Expand Down
8 changes: 4 additions & 4 deletions docs/docfx/articles/header-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Code:
{
RouteId = "route1",
ClusterId = "cluster1",
Match =
Match = new ProxyMatch
{
Path = "{**catch-all}",
Headers = new[]
Expand All @@ -108,7 +108,7 @@ Code:
{
RouteId = "route2",
ClusterId = "cluster1",
Match =
Match = new ProxyMatch
{
Path = "{**catch-all}",
Headers = new[]
Expand All @@ -126,7 +126,7 @@ Code:
{
RouteId = "route3",
ClusterId = "cluster1",
Match =
Match = new ProxyMatch
{
Path = "{**catch-all}",
Headers = new[]
Expand All @@ -143,7 +143,7 @@ Code:
{
RouteId = "route4",
ClusterId = "cluster1",
Match =
Match = new ProxyMatch
{
Path = "{**catch-all}",
Headers = new[]
Expand Down
2 changes: 1 addition & 1 deletion docs/docfx/articles/load-balancing.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var clusters = new[]
{
Id = "cluster1",
LoadBalancingPolicy = LoadBalancingPolicies.RoundRobin,
Destinations =
Destinations = new Dictionary<string, Destination>(StringComparer.OrdinalIgnoreCase)
{
{ "destination1", new Destination() { Address = "https://localhost:10000" } },
{ "destination2", new Destination() { Address = "https://localhost:10010" } }
Expand Down
51 changes: 0 additions & 51 deletions samples/ReverseProxy.Code.Sample/CustomConfigFilter.cs

This file was deleted.

9 changes: 5 additions & 4 deletions samples/ReverseProxy.Code.Sample/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -37,7 +39,7 @@ public void ConfigureServices(IServiceCollection services)
{
RouteId = "route1",
ClusterId = "cluster1",
Match =
Match = new ProxyMatch
{
Path = "{**catch-all}"
}
Expand All @@ -49,16 +51,15 @@ public void ConfigureServices(IServiceCollection services)
{
Id = "cluster1",
SessionAffinity = new SessionAffinityOptions { Enabled = true, Mode = "Cookie" },
Destinations =
Destinations = new Dictionary<string, Destination>(StringComparer.OrdinalIgnoreCase)
{
{ "destination1", new Destination() { Address = "https://localhost:10000" } }
}
}
};

services.AddReverseProxy()
.LoadFromMemory(routes, clusters)
.AddProxyConfigFilter<CustomConfigFilter>();
.LoadFromMemory(routes, clusters);

services.AddHttpContextAccessor();
services.AddSingleton<IProxyMetricsConsumer, ProxyMetricsConsumer>();
Expand Down
40 changes: 29 additions & 11 deletions samples/ReverseProxy.Config.Sample/CustomConfigFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,57 @@ namespace Microsoft.ReverseProxy.Sample
{
public class CustomConfigFilter : IProxyConfigFilter
{
public Task ConfigureClusterAsync(Cluster cluster, CancellationToken cancel)
public ValueTask<Cluster> ConfigureClusterAsync(Cluster cluster, CancellationToken cancel)
{
// How to use custom metadata to configure clusters
if (cluster.Metadata?.TryGetValue("CustomHealth", out var customHealth) ?? false
&& string.Equals(customHealth, "true", StringComparison.OrdinalIgnoreCase))
{
cluster.HealthCheck ??= new HealthCheckOptions { Active = new ActiveHealthCheckOptions() };
cluster.HealthCheck.Active.Enabled = true;
cluster.HealthCheck.Active.Policy = HealthCheckConstants.ActivePolicy.ConsecutiveFailures;
cluster = cluster with
{
HealthCheck = new HealthCheckOptions
{
Active = new ActiveHealthCheckOptions
{
Enabled = true,
Policy = HealthCheckConstants.ActivePolicy.ConsecutiveFailures,
},
Passive = cluster.HealthCheck?.Passive,
}
};
}

// Or wrap the meatadata in config sugar
var config = new ConfigurationBuilder().AddInMemoryCollection(cluster.Metadata).Build();
if (config.GetValue<bool>("CustomHealth"))
{
cluster.HealthCheck ??= new HealthCheckOptions { Active = new ActiveHealthCheckOptions() };
cluster.HealthCheck.Active.Enabled = true;
cluster.HealthCheck.Active.Policy = HealthCheckConstants.ActivePolicy.ConsecutiveFailures;
cluster = cluster with
{
HealthCheck = new HealthCheckOptions
{
Active = new ActiveHealthCheckOptions
{
Enabled = true,
Policy = HealthCheckConstants.ActivePolicy.ConsecutiveFailures,
},
Passive = cluster.HealthCheck?.Passive,
}
};
}

return Task.CompletedTask;
return new ValueTask<Cluster>(cluster);
}

public Task ConfigureRouteAsync(ProxyRoute route, CancellationToken cancel)
public ValueTask<ProxyRoute> ConfigureRouteAsync(ProxyRoute route, CancellationToken cancel)
{
// Do not let config based routes take priority over code based routes.
// Lower numbers are higher priority. Code routes default to 0.
if (route.Order.HasValue && route.Order.Value < 1)
{
route.Order = 1;
return new ValueTask<ProxyRoute>(route with { Order = 1 });
}

return Task.CompletedTask;
return new ValueTask<ProxyRoute>(route);
}
}
}
2 changes: 1 addition & 1 deletion samples/ReverseProxy.Direct.Sample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void Configure(IApplicationBuilder app, IHttpProxy httpProxy)

// Copy all request headers except Host
var transformer = new CustomTransformer(); // or HttpTransformer.Default;
var requestOptions = new RequestProxyOptions(TimeSpan.FromSeconds(100), null);
var requestOptions = new RequestProxyOptions { Timeout = TimeSpan.FromSeconds(100) };

app.UseRouting();
app.UseAuthorization();
Expand Down
17 changes: 10 additions & 7 deletions src/ReverseProxy.ServiceFabric/ServiceDiscovery/Discoverer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ public Discoverer(
continue;
}

var cluster = LabelsParser.BuildCluster(service.ServiceName, serviceExtensionLabels);
await DiscoverDestinationsAsync(cluster, options, service, serviceExtensionLabels, cancellation);
var destinations = await DiscoverDestinationsAsync(options, service, serviceExtensionLabels, cancellation);
var cluster = LabelsParser.BuildCluster(service.ServiceName, serviceExtensionLabels, destinations);
var clusterValidationErrors = await _configValidator.ValidateClusterAsync(cluster);
if (clusterValidationErrors.Count > 0)
{
Expand Down Expand Up @@ -242,8 +242,7 @@ private bool HttpsSchemeSelector(string urlScheme)
/// and populates the specified <paramref name="cluster"/>'s <see cref="Cluster.Destinations"/> accordingly.
/// </summary>
/// <remarks>All non-fatal exceptions are caught and logged.</remarks>
private async Task DiscoverDestinationsAsync(
Cluster cluster,
private async Task<IReadOnlyDictionary<string, Destination>> DiscoverDestinationsAsync(
ServiceFabricDiscoveryOptions options,
ServiceWrapper service,
Dictionary<string, string> serviceExtensionLabels,
Expand All @@ -257,9 +256,11 @@ private async Task DiscoverDestinationsAsync(
catch (Exception ex) // TODO: davidni: not fatal?
{
Log.GettingPartitionFailed(_logger, service.ServiceName, ex);
return;
return null;
}

var destinations = new Dictionary<string, Destination>(StringComparer.OrdinalIgnoreCase);

var listenerName = serviceExtensionLabels.GetValueOrDefault("YARP.Backend.ServiceFabric.ListenerName", string.Empty);
var healthListenerName = serviceExtensionLabels.GetValueOrDefault("YARP.Backend.HealthCheck.Active.ServiceFabric.ListenerName", string.Empty);
var statefulReplicaSelectionMode = ParseStatefulReplicaSelectionMode(serviceExtensionLabels, service.ServiceName);
Expand Down Expand Up @@ -297,9 +298,9 @@ private async Task DiscoverDestinationsAsync(
var destination = BuildDestination(replica, listenerName, healthListenerName);

ReportReplicaHealth(options, service, partition, replica, HealthState.Ok, $"Successfully built the endpoint from listener '{listenerName}'.");
if (!cluster.Destinations.TryAdd(replica.Id.ToString(), destination))
if (!destinations.TryAdd(replica.Id.ToString(), destination))
{
throw new ConfigException($"Duplicated endpoint id '{replica.Id}'. Skipping repeated definition, service '{service.ServiceName}', backend id '{cluster.Id}'");
throw new ConfigException($"Duplicated endpoint id '{replica.Id}'. Skipping repeated definition for service '{service.ServiceName}'.");
}
}
catch (ConfigException ex)
Expand All @@ -319,6 +320,8 @@ private async Task DiscoverDestinationsAsync(
}
}
}

return destinations;
}

private StatefulReplicaSelectionMode ParseStatefulReplicaSelectionMode(Dictionary<string, string> serviceExtensionLabels, Uri serviceName)
Expand Down
Loading