/// <summary> /// Loads (or reloads) the data for this provider. /// </summary> public async Task <IDictionary <string, string> > LoadAsync(CancellationToken cancellationToken) { var data = new Dictionary <string, string>(); IEnumerable <ApplicationWrapper> applications; try { applications = await _serviceFabricCaller.GetApplicationListAsync(cancellationToken); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { throw; } catch (Exception ex) // TODO: davidni: not fatal? { // The serviceFabricCaller does their best effort to use LKG information, nothing we can do at this point _logger.LogError(ex, "Could not get applications list from Service Fabric, continuing with zero applications."); applications = Enumerable.Empty <ApplicationWrapper>(); } foreach (var application in applications) { await LoadApplicationDataAsync(data, application, cancellationToken); } return(data); }
/// <inheritdoc/> public async Task <(IReadOnlyList <RouteConfig> Routes, IReadOnlyList <ClusterConfig> Clusters)> DiscoverAsync(CancellationToken cancellation) { // Take a snapshot of current options and use that consistently for this execution. var options = _optionsMonitor.CurrentValue; _serviceFabricCaller.CleanUpExpired(); var discoveredBackends = new Dictionary <string, ClusterConfig>(StringComparer.Ordinal); var discoveredRoutes = new List <RouteConfig>(); IEnumerable <ApplicationWrapper> applications; try { applications = await _serviceFabricCaller.GetApplicationListAsync(cancellation); } catch (OperationCanceledException) when(cancellation.IsCancellationRequested) { throw; } catch (Exception ex) // TODO: davidni: not fatal? { // The serviceFabricCaller does their best effort to use LKG information, nothing we can do at this point Log.GettingApplicationFailed(_logger, ex); applications = Enumerable.Empty <ApplicationWrapper>(); } foreach (var application in applications) { IEnumerable <ServiceWrapper> services; try { services = await _serviceFabricCaller.GetServiceListAsync(application.ApplicationName, cancellation); } catch (OperationCanceledException) when(cancellation.IsCancellationRequested) { throw; } catch (Exception ex) // TODO: davidni: not fatal? { Log.GettingServiceFailed(_logger, application.ApplicationName, ex); continue; } foreach (var service in services) { try { var serviceExtensionLabels = await _serviceFabricExtensionConfigProvider.GetExtensionLabelsAsync(application, service, cancellation); // If this service wants to use us as the proxy if (serviceExtensionLabels.GetValueOrDefault("YARP.Enable", null) != "true") { // Skip this service continue; } 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) { throw new ConfigException($"Skipping cluster id '{cluster.ClusterId} due to validation errors.", new AggregateException(clusterValidationErrors)); } if (!discoveredBackends.TryAdd(cluster.ClusterId, cluster)) { throw new ConfigException($"Duplicated cluster id '{cluster.ClusterId}'. Skipping repeated definition, service '{service.ServiceName}'"); } var routes = LabelsParser.BuildRoutes(service.ServiceName, serviceExtensionLabels); var routeValidationErrors = new List <Exception>(); foreach (var route in routes) { routeValidationErrors.AddRange(await _configValidator.ValidateRouteAsync(route)); } if (routeValidationErrors.Count > 0) { // Don't add ANY routes if even a single one is bad. Trying to add partial routes // could lead to unexpected results (e.g. a typo in the configuration of higher-priority route // could lead to a lower-priority route being selected for requests it should not be handling). throw new ConfigException($"Skipping ALL routes for cluster id '{cluster.ClusterId} due to validation errors.", new AggregateException(routeValidationErrors)); } discoveredRoutes.AddRange(routes); ReportServiceHealth(options, service.ServiceName, HealthState.Ok, $"Successfully built cluster '{cluster.ClusterId}' with {routes.Count} routes."); } catch (ConfigException ex) { // User error Log.InvalidServiceConfig(_logger, service.ServiceName, ex); // TODO: emit Error health report once we are able to detect config issues *during* (as opposed to *after*) a target service upgrade. // Proactive Error health report would trigger a rollback of the target service as desired. However, an Error report after rhe fact // will NOT cause a rollback and will prevent the target service from performing subsequent monitored upgrades to mitigate, making things worse. ReportServiceHealth(options, service.ServiceName, HealthState.Warning, $"Could not load service configuration: {ex.Message}."); } catch (Exception ex) // TODO: davidni: not fatal? { // Not user's problem Log.ErrorLoadingServiceConfig(_logger, service.ServiceName, ex); } } } Log.ServiceDiscovered(_logger, discoveredBackends.Count, discoveredRoutes.Count); return(discoveredRoutes, discoveredBackends.Values.ToList()); }