From af3c39da6a85685cc458171f2d20aa75bd7b5af9 Mon Sep 17 00:00:00 2001 From: Sergey Borodachev Date: Sat, 18 Aug 2018 04:19:41 +0400 Subject: [PATCH] Addressing issue #559 (Infinite configuration file growth when ASPNETCORE_ENVIRONMENT is defined) Made IHostingEnvironment optional for AddOcelot() to avoid breaking change. --- .../ConfigurationBuilderExtensions.cs | 30 ++++--- .../ConfigurationBuilderExtensionsTests.cs | 82 +++++++++++++++++-- 2 files changed, 93 insertions(+), 19 deletions(-) diff --git a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs index 7eb94329..6c6e2b48 100644 --- a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs +++ b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs @@ -9,6 +9,7 @@ namespace Ocelot.DependencyInjection using System.Text.RegularExpressions; using Configuration.File; using Newtonsoft.Json; + using Microsoft.AspNetCore.Hosting; public static class ConfigurationBuilderExtensions { @@ -28,32 +29,37 @@ namespace Ocelot.DependencyInjection return builder; } - public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder) + public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IHostingEnvironment env = null) { - const string pattern = "(?i)ocelot\\.([a-zA-Z0-9]*)(\\.json)$"; + const string primaryConfigFile = "ocelot.json"; - var reg = new Regex(pattern); + const string globalConfigFile = "ocelot.global.json"; - var files = Directory.GetFiles(".") - .Where(path => reg.IsMatch(path)) + const string subConfigPattern = @"^ocelot\.[a-zA-Z0-9]+\.json$"; + + string excludeConfigName = env?.EnvironmentName != null ? $"ocelot.{env.EnvironmentName}.json" : string.Empty; + + var reg = new Regex(subConfigPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); + + var files = new DirectoryInfo(".") + .EnumerateFiles() + .Where(fi => reg.IsMatch(fi.Name) && (fi.Name != excludeConfigName)) .ToList(); var fileConfiguration = new FileConfiguration(); foreach (var file in files) { - // windows and unix sigh... - if(files.Count > 1 && (file == "./ocelot.json" || file == ".\\ocelot.json")) + if(files.Count > 1 && file.Name.Equals(primaryConfigFile, StringComparison.OrdinalIgnoreCase)) { continue; } - var lines = File.ReadAllText(file); + var lines = File.ReadAllText(file.FullName); var config = JsonConvert.DeserializeObject(lines); - // windows and unix sigh... - if (file == "./ocelot.global.json" || file == ".\\ocelot.global.json") + if (file.Name.Equals(globalConfigFile, StringComparison.OrdinalIgnoreCase)) { fileConfiguration.GlobalConfiguration = config.GlobalConfiguration; } @@ -64,9 +70,9 @@ namespace Ocelot.DependencyInjection var json = JsonConvert.SerializeObject(fileConfiguration); - File.WriteAllText("ocelot.json", json); + File.WriteAllText(primaryConfigFile, json); - builder.AddJsonFile("ocelot.json"); + builder.AddJsonFile(primaryConfigFile); return builder; } diff --git a/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs index 7c29977d..630b069c 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs @@ -9,6 +9,8 @@ using Shouldly; using TestStack.BDDfy; using Xunit; + using Moq; + using Microsoft.AspNetCore.Hosting; public class ConfigurationBuilderExtensionsTests { @@ -19,6 +21,18 @@ private FileConfiguration _reRouteA; private FileConfiguration _reRouteB; private FileConfiguration _aggregate; + private FileConfiguration _envSpecific; + + public ConfigurationBuilderExtensionsTests() + { + // Clean up config files before each test + var subConfigFiles = new DirectoryInfo(".").GetFiles("ocelot.*.json"); + + foreach(var config in subConfigFiles) + { + config.Delete(); + } + } [Fact] public void should_add_base_url_to_config() @@ -32,13 +46,23 @@ [Fact] public void should_merge_files() { - this.Given(_ => GivenMultipleConfigurationFiles()) - .When(_ => WhenIAddOcelotConfiguration()) + this.Given(_ => GivenMultipleConfigurationFiles(false)) + .When(_ => WhenIAddOcelotConfiguration(false)) .Then(_ => ThenTheConfigsAreMerged()) .BDDfy(); } - private void GivenMultipleConfigurationFiles() + [Fact] + public void should_merge_files_except_env() + { + this.Given(_ => GivenMultipleConfigurationFiles(true)) + .When(_ => WhenIAddOcelotConfiguration(true)) + .Then(_ => ThenTheConfigsAreMerged()) + .And(_ => NotContainsEnvSpecificConfig()) + .BDDfy(); + } + + private void GivenMultipleConfigurationFiles(bool addEnvSpecificConfig) { _globalConfig = new FileConfiguration { @@ -140,7 +164,7 @@ { new FileAggregateReRoute { - ReRouteKeys = new List + ReRouteKeys = new List { "KeyB", "KeyBB" @@ -149,7 +173,7 @@ }, new FileAggregateReRoute { - ReRouteKeys = new List + ReRouteKeys = new List { "KeyB", "KeyBB" @@ -159,16 +183,52 @@ } }; + _envSpecific = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamScheme = "DownstreamSchemeSpec", + DownstreamPathTemplate = "DownstreamPathTemplateSpec", + Key = "KeySpec", + UpstreamHost = "UpstreamHostSpec", + UpstreamHttpMethod = new List + { + "UpstreamHttpMethodSpec" + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "HostSpec", + Port = 80 + } + } + } + } + }; + File.WriteAllText("ocelot.global.json", JsonConvert.SerializeObject(_globalConfig)); File.WriteAllText("ocelot.reRoutesA.json", JsonConvert.SerializeObject(_reRouteA)); File.WriteAllText("ocelot.reRoutesB.json", JsonConvert.SerializeObject(_reRouteB)); File.WriteAllText("ocelot.aggregates.json", JsonConvert.SerializeObject(_aggregate)); + + if (addEnvSpecificConfig) + { + File.WriteAllText("ocelot.Env.json", JsonConvert.SerializeObject(_envSpecific)); + } } - private void WhenIAddOcelotConfiguration() + private void WhenIAddOcelotConfiguration(bool addEnv) { IConfigurationBuilder builder = new ConfigurationBuilder(); - builder.AddOcelot(); + + var hostingEnvironment = new Mock(); + hostingEnvironment.SetupGet(x => x.EnvironmentName).Returns(addEnv ? "Env" : null); + + builder.AddOcelot(hostingEnvironment.Object); + _configRoot = builder.Build(); } @@ -208,6 +268,14 @@ fc.Aggregates.Count.ShouldBe(_aggregate.Aggregates.Count); } + private void NotContainsEnvSpecificConfig() + { + var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration)); + fc.ReRoutes.ShouldNotContain(x => x.DownstreamScheme == _envSpecific.ReRoutes[0].DownstreamScheme); + fc.ReRoutes.ShouldNotContain(x => x.DownstreamPathTemplate == _envSpecific.ReRoutes[0].DownstreamPathTemplate); + fc.ReRoutes.ShouldNotContain(x => x.Key == _envSpecific.ReRoutes[0].Key); + } + private void GivenTheBaseUrl(string baseUrl) { #pragma warning disable CS0618