private static Dependency HandleDependency(JsonClass dependency, IFilePathMapper pathMapper, NAMESettings configuration, NAMEContext context, int depth = 0) { if (depth == MAX_DEPENDENCY_DEPTH) { throw new NAMEException($"Reached the maximum dependency recursion of {MAX_DEPENDENCY_DEPTH}."); } if (dependency == null) { return(null); } var conditionObject = dependency["oneOf"]; if (conditionObject != null) { depth++; return(new OneOfDependency(HandleDependencyArray(conditionObject.AsArray, pathMapper, configuration, context, depth))); } var minVersion = dependency["min_version"].Value; var maxVersion = dependency["max_version"].Value; var name = dependency["name"]?.Value; var type = dependency["type"]?.Value; var osName = dependency["os_name"]?.Value; type = string.IsNullOrEmpty(type) ? SupportedDependencies.Service.ToString() : type; SupportedDependencies typedType; if (!Enum.TryParse(type, out typedType)) { throw new NAMEException($"The dependency type {type} is not supported."); } VersionedDependency result; if (typedType == SupportedDependencies.OperatingSystem) { result = new OperatingSystemDependency() { OperatingSystemName = osName, MinimumVersion = ParseVersion(minVersion, osName), MaximumVersion = ParseVersion(maxVersion, osName) }; } else { var connectionStringProvider = ParseConnectionStringProvider(dependency["connection_string"], pathMapper); result = new ConnectedDependency(GetConnectedDependencyVersionResolver(typedType, connectionStringProvider, configuration, context)) { ConnectionStringProvider = connectionStringProvider, MinimumVersion = ParseVersion(minVersion, type), MaximumVersion = ParseVersion(maxVersion, type), ShowConnectionStringInJson = configuration.ConnectedDependencyShowConnectionString }; } result.Name = name; result.Type = typedType; return(result); }
public NAMEHandler(string apiName, string apiVersion, string dependenciesFile, IFilePathMapper pathMapper, NAMESettings settings) { this.apiName = apiName; this.apiVersion = apiVersion; this.dependenciesFile = dependenciesFile; this.pathMapper = pathMapper; this.settings = settings; }
/// <summary> /// Reads the configuration. /// </summary> /// <param name="dependenciesFile">The configuration file.</param> /// <param name="pathMapper">The path mapper.</param> /// <param name="settings">The settings.</param> /// <param name="context">The context.</param> /// <returns> /// Returns the dependencies read from the file. /// </returns> /// <exception cref="NAMEException">An unhandled exception happened.</exception> /// <exception cref="System.IO.FileNotFoundException">The configuration file was not found.</exception> public static ParsedDependencies ReadDependencies(string dependenciesFile, IFilePathMapper pathMapper, NAMESettings settings, NAMEContext context) { Guard.NotNull(settings, nameof(settings)); if (context == null) { context = new NAMEContext(); } string jsonContents = ReadJsonContents(pathMapper.MapPath(dependenciesFile)); return(ParseDependenciesFromString(jsonContents, pathMapper, settings, context)); }
private static ParsedDependencies ParseDependenciesFromString( string jsonContents, IFilePathMapper pathMapper, NAMESettings settings, NAMEContext context) { JsonNode rootNode = Json.Json.Parse(jsonContents); return(new ParsedDependencies( HandleDependencyArray(rootNode?["infrastructure_dependencies"]?.AsArray, pathMapper, settings, context), HandleDependencyArray(rootNode?["service_dependencies"]?.AsArray, pathMapper, settings, context))); }
/// <summary> /// Registers the instance in one or more registars. /// </summary> /// <param name="pathMapper">The path mapper.</param> /// <param name="apiName">Name of the API.</param> /// <param name="apiVersion">The API version.</param> /// <param name="dependenciesFileLocation">The dependencies file location.</param> /// <param name="settings">The context.</param> /// <param name="hostname">The hostname.</param> /// <param name="port">The port.</param> /// <param name="nameVersion">The name version.</param> /// <param name="nameEndpoint">The name endpoint.</param> /// <param name="supportedProtocols">The supported protocols.</param> /// <exception cref="System.ArgumentException">Too many dots. - nameVersion</exception> /// <exception cref="System.ArgumentNullException">nameConfig /// or /// hostname /// or /// vpath /// or /// nameVersion /// or /// nameEndpoint /// or /// supportedProtocols</exception> public void RegisterInstance( IFilePathMapper pathMapper, string apiName, string apiVersion, string dependenciesFileLocation, NAMESettings settings, string hostname, uint?port, string nameVersion, string nameEndpoint, params uint[] supportedProtocols) { this.pathMapper = Guard.NotNull(pathMapper, nameof(pathMapper)); // todo guard this.settings = Guard.NotNull(settings, nameof(settings)); this.Hostname = Guard.NotNull(hostname, nameof(hostname)); this.NameVersion = Guard.NotNull(nameVersion, nameof(nameVersion)); this.NameEndpoint = Guard.NotNull(nameEndpoint, nameof(nameEndpoint)); this.SupportedProtocols = Guard.NotNull(supportedProtocols, nameof(supportedProtocols)); this.dependenciesFileLocation = dependenciesFileLocation; this.ApiName = apiName; this.ApiVersion = apiVersion; this.Hostname = hostname; this.Port = port; int dotsCount = nameVersion.Length - nameVersion.Replace(".", string.Empty).Length; if (dotsCount > 3) { throw new ArgumentException("Too many dots.", nameof(nameVersion)); } this.NameVersion = nameVersion.Substring(0, nameVersion.LastIndexOf('.')); this.SupportedProtocols = supportedProtocols; if (this.settings.RegistryEndpoints.Length == 0) { LogInfo("No registry endpoints to register this api", true); return; } LogInfo("RegisterInstance started", true); this.RegisterTask = Task.Factory.StartNew(this.RegisterLoop, this.cancellationTokenSource.Token); }
/// <summary> /// Reads the dependencies. /// </summary> /// <param name="configurationStream">The configuration stream.</param> /// <param name="pathMapper">The path mapper.</param> /// <param name="settings">The context information.</param> /// <param name="context">The context.</param> /// <returns> /// Returns the dependencies read from the stream. /// </returns> /// <exception cref="NAMEException">Configuration file stream is not in readable state</exception> public static ParsedDependencies ReadDependencies( Stream configurationStream, IFilePathMapper pathMapper, NAMESettings settings, NAMEContext context) { Guard.NotNull(settings, nameof(settings)); if (context == null) { context = new NAMEContext(); } if (configurationStream.CanRead == false) { throw new NAMEException("Configuration file stream is not in readable state"); } using (StreamReader reader = new StreamReader(configurationStream)) { var jsonContents = reader.ReadToEnd(); return(ParseDependenciesFromString(jsonContents, pathMapper, settings, context)); } }
/// <summary> /// Initializes a new instance of the <see cref="NAMEMiddleware" /> class. /// </summary> /// <param name="next">The next middleware in the pipeline.</param> /// <param name="nameConfiguration">The name configuration.</param> /// <param name="settings">The name settings.</param> /// <param name="pathMapper">The path mapper.</param> public NAMEMiddleware( RequestDelegate next, NAMEBaseConfiguration nameConfiguration, NAMESettings settings, IFilePathMapper pathMapper) { this.next = next; this.nameConfiguration = nameConfiguration; this.settings = settings; this.pathMapper = pathMapper; if (string.IsNullOrEmpty(nameConfiguration.ManifestUriPrefix)) { this.nameRequestMatcher = new TemplateMatcher(TemplateParser.Parse(Constants.MANIFEST_ENDPOINT.TrimStart('/')), new RouteValueDictionary()); this.nameUiRequestMatcher = new TemplateMatcher(TemplateParser.Parse(Constants.MANIFEST_UI_ENDPOINT.TrimStart('/')), new RouteValueDictionary()); } else { this.nameRequestMatcher = new TemplateMatcher(TemplateParser.Parse(nameConfiguration.ManifestUriPrefix.TrimEnd('/') + Constants.MANIFEST_ENDPOINT), new RouteValueDictionary()); this.nameUiRequestMatcher = new TemplateMatcher(TemplateParser.Parse(nameConfiguration.ManifestUriPrefix.TrimEnd('/') + Constants.MANIFEST_UI_ENDPOINT), new RouteValueDictionary()); } }
private static IList <Dependency> HandleDependencyArray( JsonArray dependencies, IFilePathMapper pathMapper, NAMESettings configuration, NAMEContext context, int depth = 0) { IList <Dependency> handledDependencies = new List <Dependency>(); if (dependencies == null) { return(handledDependencies); } foreach (JsonNode dependency in dependencies) { if (dependency.AsObject != null) { handledDependencies.Add(HandleDependency(dependency.AsObject, pathMapper, configuration, context, depth)); } } return(handledDependencies); }
/// <summary> /// Reads the NAME settings overrides. /// </summary> /// <param name="settingsFile">The settings file.</param> /// <param name="pathMapper">The path mapper.</param> /// <returns> /// Returns the <see cref="NAMESettings" />. /// </returns> public static NAMESettings ReadNAMESettingsOverrides(string settingsFile, IFilePathMapper pathMapper) { Guard.NotNull(settingsFile, nameof(settingsFile)); var jsonContents = ReadJsonContents(pathMapper.MapPath(settingsFile)); JsonNode rootNode = Json.Json.Parse(jsonContents); var overrideNode = rootNode["Overrides"]; var settings = new NAMESettings(); if (overrideNode == null) { return(settings); } var registryEndpoints = overrideNode[nameof(settings.RegistryEndpoints)]?.AsArray; if (registryEndpoints != null) { settings.RegistryEndpoints = new string[registryEndpoints.Count]; for (int i = 0; i < registryEndpoints.Count; i++) { settings.RegistryEndpoints[i] = registryEndpoints[i].Value; } } var selfhostPortRangeFirst = overrideNode[nameof(settings.SelfHostPortRangeFirst)]?.AsInt; if (selfhostPortRangeFirst != null) { settings.SelfHostPortRangeFirst = selfhostPortRangeFirst.Value; } var selfhostPortRangeLast = overrideNode[nameof(settings.SelfHostPortRangeLast)]?.AsInt; if (selfhostPortRangeLast != null) { settings.SelfHostPortRangeLast = selfhostPortRangeLast.Value; } var serviceDependencyMaxHops = overrideNode[nameof(settings.ServiceDependencyMaxHops)]?.AsInt; if (serviceDependencyMaxHops != null) { settings.ServiceDependencyMaxHops = serviceDependencyMaxHops.Value; } var serviceDependencyShowConnectionString = overrideNode[nameof(settings.ConnectedDependencyShowConnectionString)]?.AsBool; if (serviceDependencyShowConnectionString != null) { settings.ConnectedDependencyShowConnectionString = serviceDependencyShowConnectionString.Value; } var dependencyConnectTimeout = overrideNode[nameof(settings.DependencyConnectTimeout)]?.AsInt; if (dependencyConnectTimeout != null) { settings.DependencyConnectTimeout = dependencyConnectTimeout.Value; } var dependencyReadWriteTimeout = overrideNode[nameof(settings.DependencyReadWriteTimeout)]?.AsInt; if (dependencyReadWriteTimeout != null) { settings.DependencyReadWriteTimeout = dependencyReadWriteTimeout.Value; } var registryReAnnounceFrequency = overrideNode[nameof(settings.RegistryReAnnounceFrequency)]; if (registryReAnnounceFrequency != null && TimeSpan.TryParse(registryReAnnounceFrequency, out TimeSpan parsedAnnounceFreq)) { settings.RegistryReAnnounceFrequency = parsedAnnounceFreq; } var registryPingFrequency = overrideNode[nameof(settings.RegistryPingFrequency)]; if (registryPingFrequency != null && TimeSpan.TryParse(registryPingFrequency, out TimeSpan parsedPingFreq)) { settings.RegistryPingFrequency = parsedPingFreq; } var runningMode = overrideNode[nameof(settings.RunningMode)]; if (runningMode != null && Enum.TryParse <SupportedNAMEBehaviours>(runningMode.Value.ToString(), false, out var behaviour)) { settings.RunningMode = behaviour; } var registryBootstrapRetryFrequency = overrideNode[nameof(settings.RegistryBootstrapRetryFrequency)]; if (registryBootstrapRetryFrequency != null && TimeSpan.TryParse(registryBootstrapRetryFrequency, out TimeSpan bootstrapRetryFreq)) { settings.RegistryBootstrapRetryFrequency = bootstrapRetryFreq; } var registryBootstrapConnectTimeout = overrideNode[nameof(settings.RegistryBootstrapTimeout)]; if (registryBootstrapConnectTimeout != null && TimeSpan.TryParse(registryBootstrapConnectTimeout, out TimeSpan bootstrapConnectTimeout)) { settings.RegistryBootstrapTimeout = bootstrapConnectTimeout; } return(settings); }
private static IConnectionStringProvider ParseConnectionStringProvider(JsonNode node, IFilePathMapper pathMapper) { if (node.AsObject == null) { return(new StaticConnectionStringProvider(node.Value)); } JsonClass objClass = node.AsObject; SupportedConnectionStringLocators locator; if (!Enum.TryParse(node["locator"]?.Value, out locator)) { throw new NAMEException($"The locator {node["locator"]?.Value} is not supported."); } IConnectionStringProvider provider = null; string key; switch (locator) { #if NET45 case SupportedConnectionStringLocators.ConnectionStrings: key = node["key"]?.Value; provider = new ConnectionStringsConnectionStringProvider(key); break; case SupportedConnectionStringLocators.AppSettings: key = node["key"]?.Value; provider = new AppSettingsConnectionStringProvider(key); break; case SupportedConnectionStringLocators.VSSettingsFile: key = node["key"]?.Value; string section = node["section"]?.Value; if (string.IsNullOrEmpty(section)) { throw new ArgumentNullException("section", "The section must be specified."); } provider = new VisualStudioSetingsFileConnectionStringProvider(section, key); break; #endif case SupportedConnectionStringLocators.JSONPath: { key = node["expression"]?.Value; string file = node["file"]?.Value; if (string.IsNullOrEmpty(file)) { throw new ArgumentNullException("file", "The file must be specified."); } provider = new JsonPathConnectionStringProvider(pathMapper.MapPath(file), key); } break; case SupportedConnectionStringLocators.XPath: { key = node["expression"]?.Value; string file = node["file"]?.Value; if (string.IsNullOrEmpty(file)) { throw new ArgumentNullException("file", "The file must be specified."); } provider = new XpathConnectionStringProvider(pathMapper.MapPath(file), key); } break; default: throw new NAMEException($"The locator {locator.ToString()} is not supported.."); } if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException($"The connection string key/expression must be specified."); } return(provider); }
public static ParsedDependencies Initialize(ISelfHostServer server, IFilePathMapper pathMapper, NAMESelfHostConfiguration configuration) { NAMESettings settings; ParsedDependencies dependencies = ReadAndLogDependencies(configuration, configuration.LogHealthCheckToConsole, pathMapper, out settings); if (settings.RunningMode == SupportedNAMEBehaviours.NAMEDisabled) { LogInfo("Not starting NAME since it is disabled in the dependencies file.", configuration.LogHealthCheckToConsole); return(dependencies); } int portNumber = settings.SelfHostPortRangeFirst; try { bool result = false; LogInfo($"Starting the server", configuration.LogHealthCheckToConsole); while (portNumber <= settings.SelfHostPortRangeLast) { result = server.Start(portNumber, settings); if (result) { break; } portNumber++; } if (result == false) { throw new NAMEException("Tried all ports, without success."); } Console.WriteLine($"Succesfully started the server. Listening on {portNumber}."); } catch (Exception ex) { LogWarning($"Could not bind to the SelfHost address: {ex.Message}.", configuration.LogHealthCheckToConsole); if (configuration.ThrowOnDependenciesFail) { if (ex is NAMEException) { throw; } else { throw new NAMEException("Could not bind to the SelfHost address.", ex); } } return(dependencies); } if (settings.RunningMode < SupportedNAMEBehaviours.BootstrapDisabled) { var register = new Registration.Register(); register.RegisterInstance( pathMapper, configuration.APIName, configuration.APIVersion, configuration.DependenciesFilePath, settings, Environment.MachineName, (uint)portNumber, typeof(SelfHostInitializer).GetTypeInfo().Assembly.GetName().Version.ToString(), (configuration.ManifestUriPrefix?.TrimEnd('/') ?? string.Empty) + Constants.MANIFEST_ENDPOINT, Constants.REGISTRY_SUPPORTED_PROTOCOL_VERSIONS); } if (settings.RunningMode == SupportedNAMEBehaviours.NAMEDisabled) { LogInfo("Stopping the NAME self host server since it is disabled in the Registry.", configuration.LogHealthCheckToConsole); server.Dispose(); return(dependencies); } return(dependencies); }
/// <summary> /// Reads the and log dependencies. /// </summary> /// <param name="configuration">The configuration.</param> /// <param name="logToConsole">if set to <c>true</c> [log to console].</param> /// <param name="pathMapper">The path mapper.</param> /// <param name="settings">The context information.</param> /// <returns> /// The <see cref="ParsedDependencies" /> object populated from the dependencies file /// </returns> /// <exception cref="NAMEException">Error parsing the dependencies file.</exception> /// <exception cref="DependenciesCheckException">Wrapper for all possible exceptions in the NAME process</exception> public static ParsedDependencies ReadAndLogDependencies(NAMEBaseConfiguration configuration, bool logToConsole, IFilePathMapper pathMapper, out NAMESettings settings) { var dependencies = new ParsedDependencies(null, null); settings = DependenciesReader.ReadNAMESettingsOverrides(configuration.DependenciesFilePath, pathMapper); try { dependencies = DependenciesReader.ReadDependencies(configuration.DependenciesFilePath, pathMapper, settings, new NAMEContext()); } catch (NAMEException ex) { LogWarning($"Could not parse the dependencies file: {ex.Message}.", logToConsole); if (configuration.ThrowOnDependenciesFail) { if (ex is NAMEException) { throw; } else { throw new NAMEException("Error parsing the dependencies file.", ex); } } return(dependencies); } if (settings.RunningMode == SupportedNAMEBehaviours.NAMEDisabled) { LogInfo("NAME was disabled in the dependencies file.", logToConsole); return(dependencies); } LogInfo("Starting the dependencies state logs.", logToConsole); var allStatuses = LogDependenciesStatuses(dependencies.InfrastructureDependencies, logToConsole); allStatuses.AddRange(LogDependenciesStatuses(dependencies.ServiceDependencies, logToConsole)); if (configuration.ThrowOnDependenciesFail && allStatuses.Any(s => !s.CheckPassed)) { throw new DependenciesCheckException(allStatuses); } return(dependencies); }
public KestrelServer(NAMEKestrelConfiguration nameConfiguration, IFilePathMapper pathMapper) { this.pathMapper = pathMapper; this.nameConfiguration = nameConfiguration; }
public HttpListenerServer(NAMEHttpListenerConfiguration nameConfiguration, IFilePathMapper pathMapper) { this.pathMapper = pathMapper; this.nameConfiguration = nameConfiguration; }
/// <summary> /// Reads the and log dependencies. /// </summary> /// <param name="configuration">The configuration.</param> /// <param name="logToConsole">if set to <c>true</c> [log to console].</param> /// <param name="pathMapper">The path mapper.</param> /// <param name="settings">The context information.</param> /// <returns> /// The <see cref="ParsedDependencies" /> object populated from the dependencies file /// </returns> /// <exception cref="NAMEException">Error parsing the dependencies file.</exception> /// <exception cref="DependenciesCheckException">Wrapper for all possible exceptions in the NAME process</exception> public static ParsedDependencies ReadAndLogDependencies(NAMEBaseConfiguration configuration, bool logToConsole, IFilePathMapper pathMapper, out NAMESettings settings) { var dependencies = new ParsedDependencies(null, null); settings = DependenciesReader.ReadNAMESettingsOverrides(configuration.DependenciesFilePath, pathMapper); try { dependencies = DependenciesReader.ReadDependencies(configuration.DependenciesFilePath, pathMapper, settings, new NAMEContext()); } catch (NAMEException ex) { LogWarning($"Could not parse the dependencies file: {ex.Message}.", logToConsole); if (configuration.ThrowOnDependenciesFail) { if (ex is NAMEException) { throw; } else { throw new NAMEException("Error parsing the dependencies file.", ex, NAMEStatusLevel.Error); } } return(dependencies); } if (settings.RunningMode == SupportedNAMEBehaviours.NAMEDisabled) { LogInfo("NAME was disabled in the dependencies file.", logToConsole); return(dependencies); } LogInfo("Starting the dependencies state logs.", logToConsole); Func <IEnumerable <DependencyCheckStatus> > logStatusesAction = () => { var allStatuses = LogDependenciesStatuses(dependencies.InfrastructureDependencies, logToConsole); allStatuses.AddRange(LogDependenciesStatuses(dependencies.ServiceDependencies, logToConsole)); return(allStatuses); }; if (configuration.ThrowOnDependenciesFail) { var allStatuses = logStatusesAction(); if (allStatuses.Any(s => s.CheckStatus != NAMEStatusLevel.Ok)) { throw new DependenciesCheckException(allStatuses); } } else { Task.Factory .StartNew(logStatusesAction) .ContinueWith( task => { LogWarning("Exception logging dependencies status:", logToConsole); var flattened = task.Exception.Flatten(); flattened.Handle(ex => { LogWarning(ex.Message, logToConsole); return(true); }); }, TaskContinuationOptions.OnlyOnFaulted); } return(dependencies); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IFilePathMapper pathMapper, NAMEKestrelConfiguration configuration, NAMESettings settings) { app.UseMiddleware <NAMEMiddleware>(configuration, settings, pathMapper); }