public async Task ApplyConfigurationsAsync_OneClusterOneDestinationOneRoute_Works() { // Arrange const string TestAddress = "https://localhost:123/"; var cluster = new Cluster { Destinations = { { "d1", new Destination { Address = TestAddress } } } }; var route = new ParsedRoute { RouteId = "route1", ClusterId = "cluster1", }; var dynamicConfigRoot = new DynamicConfigRoot { Clusters = new Dictionary<string, Cluster> { { "cluster1", cluster } }, Routes = new[] { route }, }; Mock<IDynamicConfigBuilder>() .Setup(d => d.BuildConfigAsync(It.IsAny<CancellationToken>())) .ReturnsAsync(dynamicConfigRoot); var proxyManager = Create<ReverseProxyConfigManager>(); // Act await proxyManager.ApplyConfigurationsAsync(CancellationToken.None); // Assert var actualClusters = _clusterManager.GetItems(); Assert.Single(actualClusters); Assert.Equal("cluster1", actualClusters[0].ClusterId); Assert.NotNull(actualClusters[0].DestinationManager); Assert.NotNull(actualClusters[0].Config.Value); var actualDestinations = actualClusters[0].DestinationManager.GetItems(); Assert.Single(actualDestinations); Assert.Equal("d1", actualDestinations[0].DestinationId); Assert.NotNull(actualDestinations[0].Config); Assert.Equal(TestAddress, actualDestinations[0].Config.Address); var actualRoutes = _routeManager.GetItems(); Assert.Single(actualRoutes); Assert.Equal("route1", actualRoutes[0].RouteId); Assert.NotNull(actualRoutes[0].Config.Value); Assert.Same(actualClusters[0], actualRoutes[0].Config.Value.Cluster); }
private void CreateEndpoints() { var routes = _routeManager.GetItems(); var endpoints = new List <Endpoint>(routes.Count); foreach (var existingRoute in routes) { // Only rebuild the endpoint for modified routes or clusters. var endpoint = existingRoute.CachedEndpoint; if (endpoint == null) { endpoint = _proxyEndpointFactory.CreateEndpoint(existingRoute.Config, _conventions); existingRoute.CachedEndpoint = endpoint; } endpoints.Add(endpoint); } UpdateEndpoints(endpoints); }
public async Task ApplyConfigurationsAsync_OneBackendOneEndpointOneRoute_Works() { // Arrange const string TestAddress = "https://localhost:123/"; var backend = new Backend { Endpoints = { { "ep1", new BackendEndpoint { Address = TestAddress } } } }; var route = new ParsedRoute { RouteId = "route1", BackendId = "backend1", }; var dynamicConfigRoot = new DynamicConfigRoot { Backends = new Dictionary <string, Backend> { { "backend1", backend } }, Routes = new[] { route }, }; Mock <IDynamicConfigBuilder>() .Setup(d => d.BuildConfigAsync(It.IsAny <IConfigErrorReporter>(), It.IsAny <CancellationToken>())) .ReturnsAsync(Result.Success(dynamicConfigRoot)); var errorReporter = new TestConfigErrorReporter(); var proxyManager = Create <ReverseProxyConfigManager>(); // Act var result = await proxyManager.ApplyConfigurationsAsync(errorReporter, CancellationToken.None); // Assert Assert.True(result); var actualBackends = _backendManager.GetItems(); Assert.Single(actualBackends); Assert.Equal("backend1", actualBackends[0].BackendId); Assert.NotNull(actualBackends[0].EndpointManager); Assert.NotNull(actualBackends[0].Config.Value); var actualEndpoints = actualBackends[0].EndpointManager.GetItems(); Assert.Single(actualEndpoints); Assert.Equal("ep1", actualEndpoints[0].EndpointId); Assert.NotNull(actualEndpoints[0].Config.Value); Assert.Equal(TestAddress, actualEndpoints[0].Config.Value.Address); var actualRoutes = _routeManager.GetItems(); Assert.Single(actualRoutes); Assert.Equal("route1", actualRoutes[0].RouteId); Assert.NotNull(actualRoutes[0].Config.Value); Assert.Same(actualBackends[0], actualRoutes[0].Config.Value.BackendOrNull); }
private void UpdateRuntimeRoutes(DynamicConfigRoot config) { var desiredRoutes = new HashSet <string>(StringComparer.Ordinal); var changed = false; foreach (var configRoute in config.Routes) { desiredRoutes.Add(configRoute.RouteId); // Note that this can be null, and that is fine. The resulting route may match // but would then fail to route, which is exactly what we were instructed to do in this case // since no valid backend was specified. var backendOrNull = _backendManager.TryGetItem(configRoute.BackendId); _routeManager.GetOrCreateItem( itemId: configRoute.RouteId, setupAction: route => { var currentRouteConfig = route.Config.Value; if (currentRouteConfig == null || currentRouteConfig.HasConfigChanged(configRoute, backendOrNull)) { // Config changed, so update runtime route changed = true; if (currentRouteConfig == null) { _logger.LogDebug("Route {routeId} has been added.", configRoute.RouteId); } else { _logger.LogDebug("Route {routeId} has changed.", configRoute.RouteId); } var newConfig = _routeEndpointBuilder.Build(configRoute, backendOrNull, route); route.Config.Value = newConfig; } }); } foreach (var existingRoute in _routeManager.GetItems()) { if (!desiredRoutes.Contains(existingRoute.RouteId)) { // NOTE 1: This is safe to do within the `foreach` loop // because `IRouteManager.GetItems` returns a copy of the list of routes. // // NOTE 2: Removing the route from `IRouteManager` is safe and existing // ASP .NET Core endpoints will continue to work with their existing behavior since // their copy of `RouteConfig` is immutable and remains operational in whichever state is was in. _logger.LogDebug("Route {routeId} has been removed.", existingRoute.RouteId); _routeManager.TryRemoveItem(existingRoute.RouteId); changed = true; } } if (changed) { var aspNetCoreEndpoints = new List <AspNetCore.Http.Endpoint>(); foreach (var existingRoute in _routeManager.GetItems()) { var runtimeConfig = existingRoute.Config.Value; if (runtimeConfig?.AspNetCoreEndpoints != null) { aspNetCoreEndpoints.AddRange(runtimeConfig.AspNetCoreEndpoints); } } // This is where the new routes take effect! _dynamicEndpointDataSource.Update(aspNetCoreEndpoints); } }