/// <inheritdoc/> public async Task <Dictionary <string, string> > GetExtensionLabelsAsync(ApplicationWrapper application, ServiceWrapper service, CancellationToken cancellationToken) { _ = application ?? throw new ArgumentNullException(nameof(application)); _ = service ?? throw new ArgumentNullException(nameof(service)); _ = application.ApplicationTypeName ?? throw new ArgumentNullException($"{nameof(application)}.{nameof(application.ApplicationTypeName)}"); _ = application.ApplicationTypeVersion ?? throw new ArgumentNullException($"{nameof(application)}.{nameof(application.ApplicationTypeVersion)}"); _ = service.ServiceTypeName ?? throw new ArgumentNullException($"{nameof(service)}.{nameof(service.ServiceTypeName)}"); _ = service.ServiceName ?? throw new ArgumentNullException($"{nameof(service)}.{nameof(service.ServiceName)}"); string serviceManifestName; try { serviceManifestName = await _serviceFabricCaller.GetServiceManifestName(application.ApplicationTypeName, application.ApplicationTypeVersion, service.ServiceTypeName, cancellationToken); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { throw; } catch (Exception ex) // TODO: davidni: not fatal? { throw new ServiceFabricIntegrationException($"Failed to get service manifest name for service type {service.ServiceTypeName} of application type {application.ApplicationTypeName} {application.ApplicationTypeVersion} from Service Fabric: {ex}."); } if (serviceManifestName == null) { throw new ServiceFabricIntegrationException($"No service manifest name was found for service type {service.ServiceTypeName} of application type {application.ApplicationTypeName} {application.ApplicationTypeVersion}."); } string rawServiceManifest; try { rawServiceManifest = await _serviceFabricCaller.GetServiceManifestAsync(application.ApplicationTypeName, application.ApplicationTypeVersion, serviceManifestName, cancellationToken); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { throw; } catch (Exception ex) // TODO: davidni: not fatal? { throw new ServiceFabricIntegrationException($"Failed to get service manifest {serviceManifestName} of service type {service.ServiceTypeName} of application type {application.ApplicationTypeName} {application.ApplicationTypeVersion} from Service Fabric: {ex}."); } if (rawServiceManifest == null) { throw new ServiceFabricIntegrationException($"No service manifest named '{serviceManifestName}' was found for service type {service.ServiceTypeName} of application type {application.ApplicationTypeName} {application.ApplicationTypeVersion}."); } // TODO: gathering labels from multiple servicetypes within the same service would result in multiple // calls to the SF client and multiple XML parses. We should consider creating an instance of this class // per application type to reuse that data. Since this is uncommon, for now we follow the na�ve implementation. var result = ExtractLabels(rawServiceManifest, service.ServiceTypeName); ApplyAppParamReplacements(result, application, service); if (result.GetValueOrDefault("YARP.EnableDynamicOverrides", null)?.ToLower() == "true") { // Override with properties IDictionary <string, string> properties; try { properties = await _serviceFabricCaller.EnumeratePropertiesAsync(service.ServiceName, cancellationToken); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { throw; } catch (Exception ex) { throw new ServiceFabricIntegrationException($"Failed to get properties for {service.ServiceName}.", ex); } OverrideLabels(ref result, properties); } return(result); }
/// <summary> /// The Service Manifest can specify a label with value <c>[AppParamName]</c>, in which case we replace it /// with the value of an application parameter with the given name <c>AppParamName</c>. /// Application parameter names are case insensitive in Service Fabric. /// If no such app param exists, we replace with empty string. /// </summary> private void ApplyAppParamReplacements(Dictionary <string, string> labels, ApplicationWrapper app, ServiceWrapper service) { var replacements = new List <KeyValuePair <string, string> >(); foreach (var label in labels) { var value = label.Value; if (value.Length > 2 && value[0] == '[' && value[value.Length - 1] == ']') { var appParamName = value.Substring(1, value.Length - 2); if (app.ApplicationParameters == null || !app.ApplicationParameters.TryGetValue(appParamName, out var appParamValue)) { // TODO: This should trigger a Warning or Error health report on the faulty service. // This is not critical because if the absence of the setting leads to invalid configs, we *do* already report error // (for example, if a route's rule were missing). Log.InvalidApplicationParameter(_logger, app.ApplicationName, app.ApplicationTypeName, app.ApplicationTypeVersion, service.ServiceName, label.Key, appParamName); appParamValue = string.Empty; } replacements.Add(KeyValuePair.Create(label.Key, appParamValue)); } } foreach (var replacement in replacements) { labels[replacement.Key] = replacement.Value; } }