-
Notifications
You must be signed in to change notification settings - Fork 855
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
Add support for adding metadata to proxy routes #328
Conversation
I hadn't realized this would be so complex a change. It's going to take me some time to go over this. The firstTime parameter is concerning, it seems like a blunt way to address a race condition. The design changes proposed in #277 should remove this race because they reverse the model from push to pull. I think we'll want to do #227 before continuing work on this PR. |
I don't like the |
But where would you call OnChange? |
Maybe in |
That's the trick, LoadFromConfig doesn't have access to IOptionsMonitor, the DI container hasn't been built yet. Something has to resolve IOptionsMonitor from DI at startup in order to kick of the process. The IHostedService is one way of doing that, but #227 should be able to replace that with a direct config source resolved from DI. |
@Kahbazi can you please rebase this? |
31069c4
to
8b1590f
Compare
I did the rebase. I need some feedback and then I will fix and add tests. |
There's another merge conflict? |
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
8b1590f
to
19a7313
Compare
src/ReverseProxy/Configuration/DependencyInjection/ReverseProxyServiceCollectionExtensions.cs
Outdated
Show resolved
Hide resolved
19a7313
to
dc2334b
Compare
I think we're going to need an explanation of the overall design that comes out of this change since it looks like it's evolved a bunch from the original purpose of the PR. |
The purpose of this PR is to enable users to add their own Metadata to Endpoints that are created by YARP and it can be done by - void MapReverseProxy(...)
+ ReverseProxyConventionBuilder MapReverseProxy(...) The challenge was that when calling This is the API that user can use to add public class ReverseProxyConventionBuilder
{
// Configure all routes
public void Add(Action<EndpointBuilder> convention);
// Configure all routes based on their Cluster
public ReverseProxyConventionBuilder ConfigureClusters(Action<IEndpointConventionBuilder, Cluster> convention);
// Configure all routes based on their Route
public ReverseProxyConventionBuilder ConfigureRoutes(Action<IEndpointConventionBuilder, ProxyRoute> convention);
// Configure all routes for a specific Cluster
public ReverseProxyConventionBuilder ConfigureCluster(string clusterId, Action<IEndpointConventionBuilder> convention);
// Configure a specific Route
public ReverseProxyConventionBuilder ConfigureRoute(string routeId, Action<IEndpointConventionBuilder> convention);
} |
I like the design. Do those configure callbacks get run every time the configuration changes? |
If the configuration changes causes a change on routes. reverse-proxy/src/ReverseProxy/Service/Management/ProxyConfigManager.cs Lines 167 to 171 in dc2334b
reverse-proxy/src/ReverseProxy/Service/Management/ProxyConfigManager.cs Lines 103 to 113 in dc2334b
|
src/ReverseProxy/Configuration/ReverseProxyIEndpointRouteBuilderExtensions.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ProxyEndpointFactory.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ProxyEndpointFactory.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ProxyEndpointFactory.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
var hasChanged = await ApplyConfigAsync(newConfig); | ||
if (hasChanged) | ||
{ | ||
CreateEndpoints(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If an individual route or cluster changes we shouldn't have to re-create every endpoint, only those that changed. That's why the endpoints were stashed on the RouteConfig before. What gets recreated is the list of endpoints.
There are callbacks to configure an endpoint route based on cluster id or data, but this doesn't detect changes to clusters so those wouldn't re-run unless the route itself changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do you suggest we should stash the endpoints now that RouteConfig is too early to create the endpoints?
Also do we need to re-create the endpoints of a cluster if its destinations changed?
2a6a87b
to
007fdb0
Compare
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
|
||
var cluster = routeConfig.Cluster?.Config.Cluster; | ||
var proxyRoute = routeConfig.ProxyRoute; | ||
if (proxyRoute != null && cluster != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's technically possible for a route not to be assigned to a cluster, but why would proxyRoute ever be null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the null check for proxy route
} | ||
|
||
/// <summary> | ||
/// Configures the endpoint for all routes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Configures the endpoint for all routes | |
/// Configures the endpoints for all routes |
/// <param name="routeId">The route id to aplly convention to its endpoint.</param> | ||
/// <param name="convention">The convention to add to the builder.</param> | ||
/// <returns></returns> | ||
public ReverseProxyConventionBuilder ConfigureRoute(string routeId, Action<IEndpointConventionBuilder> convention) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The experience question we need to answer is how common you'll write code to configure a route or cluster by name vs inspecting all the endpoints and reacting to other fields like metadata. i.e. do we need all 9 of these overloads, or only the first three Configure ones? The cluster and route specific ones can be built on top of the first three Configure conventions if needed.
I'd suggest limiting it to just the Configure overloads for now. We can revisit as we see demand for common variations.
test/ReverseProxy.Tests/Service/Management/ProxyConfigManagerTests.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
7160a75
to
c24e188
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design update:
MapReverseProxy now returns a ReverseProxyConventionBuilder : IEndpointConventionBuilder
. This has a new Configure
API with three overloads. The purpose of the API is to let you provide one or more conventions that examine all of the routes loaded from config and append any custom endpoint metadata those routes need.
E.g. A convention could check for all routes that use cluster1 and set AllowAnonymous auth for those endpoints, where auth is normally configured per route. A developer could also insert their own endpoint metadata based on route or cluster extensible metadata fields.
New API:
Configure(Action<IEndpointConventionBuilder> convention)
Configure(Action<IEndpointConventionBuilder, ProxyRoute> convention)
Configure(Action<IEndpointConventionBuilder, ProxyRoute, Cluster> convention)
Clarifications from the prior discussion:
- The ProxyRoute and Cluster types are given here for information purposes, they're not modifiable at this point the the lifecycle. Use freezable pattern for contract types instead of deep cloning #179 tracks clarifying that.
- We don't want to define new routes in Startup, we want routes to be sourced from an external store so they can always be updated without restarting.
- When routes or clusters are updated, the associated endpoints are recreated and the conventions re-run.
- The overloads that reference individual routes or clusters by name have been removed. While it's possible to make decisions based on names, I don't think that's the primary use case, but rather to use config metadata from the routes and clusters.
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor comments
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Outdated
Show resolved
Hide resolved
src/ReverseProxy/Service/DynamicEndpoint/ReverseProxyConventionBuilder.cs
Show resolved
Hide resolved
a13539b
to
f4c21d0
Compare
Hello @Tratcher! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
@Kahbazi thanks for the hard work on this one, I'm glad we were able to work through it eventually. |
Partially Fixes #286
I changed the way endpoints are being built. I tried to mirror MVC, and now endpoints are being built in
ProxyDynamicEndpointDataSource
instead ofRuntimeRouteBuilder
.I think the main challenge is with
ProxyConfigLoader
which is aIHostedService
and runs beforeMapReverseProxy
has a chance to add metadata, so I add abool firstTime
parameter and pass in around , so I only updateRouteConfig
and not building the endpoints.I want feedback for design before I continue with unit tests.