mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-23 00:52:51 +08:00
* #463 save both files * #463 made it so we dont save to disk on startup unless using admin api
This commit is contained in:
parent
9f4448378a
commit
1817564ea5
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,7 +6,7 @@
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
.DS_Store
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
|
@ -98,12 +98,14 @@ This gets the current Ocelot configuration. It is exactly the same JSON we use t
|
||||
|
||||
**POST {adminPath}/configuration**
|
||||
|
||||
|
||||
This overrwrites the existing configuration (should probably be a put!). I reccomend getting your config from the GET endpoint, making any changes and posting it back...simples.
|
||||
|
||||
The body of the request is JSON and it is the same format as the FileConfiguration.cs that we use to set up
|
||||
Ocelot on a file system.
|
||||
|
||||
Please note that if you want to use this API then the process running Ocelot must have permission to write to the disk
|
||||
where your ocelot.json or ocelot.{environment}.json is located. This is because Ocelot will overwrite them on save.
|
||||
|
||||
**DELETE {adminPath}/outputcache/{region}**
|
||||
|
||||
This clears a region of the cache. If you are using a backplane it will clear all instances of the cache! Giving your the ability to run a cluster of Ocelots and cache over all of them in memory and clear them all at the same time / just use a distributed cache.
|
||||
|
@ -9,15 +9,16 @@ namespace Ocelot.Configuration.Repository
|
||||
{
|
||||
public class DiskFileConfigurationRepository : IFileConfigurationRepository
|
||||
{
|
||||
private readonly string _configFilePath;
|
||||
|
||||
private readonly string _environmentFilePath;
|
||||
private readonly string _ocelotFilePath;
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
private const string ConfigurationFileName = "ocelot";
|
||||
|
||||
public DiskFileConfigurationRepository(IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
_configFilePath = $"{AppContext.BaseDirectory}/{ConfigurationFileName}{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
|
||||
_environmentFilePath = $"{AppContext.BaseDirectory}{ConfigurationFileName}{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
|
||||
|
||||
_ocelotFilePath = $"{AppContext.BaseDirectory}{ConfigurationFileName}.json";
|
||||
}
|
||||
|
||||
public Task<Response<FileConfiguration>> Get()
|
||||
@ -26,7 +27,7 @@ namespace Ocelot.Configuration.Repository
|
||||
|
||||
lock(_lock)
|
||||
{
|
||||
jsonConfiguration = System.IO.File.ReadAllText(_configFilePath);
|
||||
jsonConfiguration = System.IO.File.ReadAllText(_environmentFilePath);
|
||||
}
|
||||
|
||||
var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(jsonConfiguration);
|
||||
@ -40,12 +41,19 @@ namespace Ocelot.Configuration.Repository
|
||||
|
||||
lock(_lock)
|
||||
{
|
||||
if (System.IO.File.Exists(_configFilePath))
|
||||
if (System.IO.File.Exists(_environmentFilePath))
|
||||
{
|
||||
System.IO.File.Delete(_configFilePath);
|
||||
System.IO.File.Delete(_environmentFilePath);
|
||||
}
|
||||
|
||||
System.IO.File.WriteAllText(_configFilePath, jsonConfiguration);
|
||||
System.IO.File.WriteAllText(_environmentFilePath, jsonConfiguration);
|
||||
|
||||
if (System.IO.File.Exists(_ocelotFilePath))
|
||||
{
|
||||
System.IO.File.Delete(_ocelotFilePath);
|
||||
}
|
||||
|
||||
System.IO.File.WriteAllText(_ocelotFilePath, jsonConfiguration);
|
||||
}
|
||||
|
||||
return Task.FromResult<Response>(new OkResponse());
|
||||
|
@ -6,6 +6,7 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Diagnostics;
|
||||
using DependencyInjection;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Creator;
|
||||
@ -115,22 +116,32 @@
|
||||
var internalConfigRepo = (IInternalConfigurationRepository)builder.ApplicationServices.GetService(typeof(IInternalConfigurationRepository));
|
||||
internalConfigRepo.AddOrReplace(internalConfig.Data);
|
||||
|
||||
var fileConfigSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter));
|
||||
|
||||
var fileConfigRepo = (IFileConfigurationRepository)builder.ApplicationServices.GetService(typeof(IFileConfigurationRepository));
|
||||
|
||||
var adminPath = (IAdministrationPath)builder.ApplicationServices.GetService(typeof(IAdministrationPath));
|
||||
|
||||
if (UsingConsul(fileConfigRepo))
|
||||
{
|
||||
//Lots of jazz happens in here..check it out if you are using consul to store your config.
|
||||
await SetFileConfigInConsul(builder, fileConfigRepo, fileConfig, internalConfigCreator, internalConfigRepo);
|
||||
}
|
||||
else
|
||||
else if(AdministrationApiInUse(adminPath))
|
||||
{
|
||||
//We have to make sure the file config is set for the ocelot.env.json and ocelot.json so that if we pull it from the
|
||||
//admin api it works...boy this is getting a spit spags boll.
|
||||
var fileConfigSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter));
|
||||
|
||||
await SetFileConfig(fileConfigSetter, fileConfig);
|
||||
}
|
||||
|
||||
return GetOcelotConfigAndReturn(internalConfigRepo);
|
||||
}
|
||||
|
||||
private static bool AdministrationApiInUse(IAdministrationPath adminPath)
|
||||
{
|
||||
return adminPath.GetType() != typeof(NullAdministrationPath);
|
||||
}
|
||||
|
||||
private static async Task SetFileConfigInConsul(IApplicationBuilder builder,
|
||||
IFileConfigurationRepository fileConfigRepo, IOptions<FileConfiguration> fileConfig,
|
||||
IInternalConfigurationCreator internalConfigCreator, IInternalConfigurationRepository internalConfigRepo)
|
||||
@ -179,8 +190,7 @@
|
||||
|
||||
private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptions<FileConfiguration> fileConfig)
|
||||
{
|
||||
Response response;
|
||||
response = await fileConfigSetter.Set(fileConfig.Value);
|
||||
var response = await fileConfigSetter.Set(fileConfig.Value);
|
||||
|
||||
if (IsError(response))
|
||||
{
|
||||
|
100
test/Ocelot.AcceptanceTests/StartupTests.cs
Normal file
100
test/Ocelot.AcceptanceTests/StartupTests.cs
Normal file
@ -0,0 +1,100 @@
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Configuration.Repository;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
using Responses;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class StartupTests : IDisposable
|
||||
{
|
||||
private readonly Steps _steps;
|
||||
private readonly ServiceHandler _serviceHandler;
|
||||
private string _downstreamPath;
|
||||
|
||||
public StartupTests()
|
||||
{
|
||||
_serviceHandler = new ServiceHandler();
|
||||
_steps = new Steps();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_not_try_and_write_to_disk_on_startup_when_not_using_admin_api()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 52179,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var fakeRepo = new FakeFileConfigurationRepository();
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52179", "/", 200, "Hello from Laura"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunningWithBlowingUpDiskRepo(fakeRepo))
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
||||
{
|
||||
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||
|
||||
if (_downstreamPath != basePath)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_serviceHandler?.Dispose();
|
||||
_steps.Dispose();
|
||||
}
|
||||
|
||||
class FakeFileConfigurationRepository : IFileConfigurationRepository
|
||||
{
|
||||
public Task<Response<FileConfiguration>> Get()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<Response> Set(FileConfiguration fileConfiguration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ using static Ocelot.Infrastructure.Wait;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
using Configuration.Repository;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
|
||||
|
||||
@ -928,5 +929,35 @@ namespace Ocelot.AcceptanceTests
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
content.ShouldBe(expectedBody);
|
||||
}
|
||||
|
||||
public void GivenOcelotIsRunningWithBlowingUpDiskRepo(IFileConfigurationRepository fake)
|
||||
{
|
||||
_webHostBuilder = new WebHostBuilder();
|
||||
|
||||
_webHostBuilder
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
{
|
||||
s.AddSingleton<IFileConfigurationRepository>(fake);
|
||||
s.AddOcelot();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
});
|
||||
|
||||
_ocelotServer = new TestServer(_webHostBuilder);
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,9 +276,23 @@ namespace Ocelot.IntegrationTests
|
||||
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
|
||||
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
|
||||
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
|
||||
.And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheConfigurationIsSavedCorrectly(FileConfiguration expected)
|
||||
{
|
||||
var ocelotJsonPath = $"{AppContext.BaseDirectory}ocelot.json";
|
||||
var resultText = File.ReadAllText(ocelotJsonPath);
|
||||
var expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented);
|
||||
resultText.ShouldBe(expectedText);
|
||||
|
||||
var environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot.Production.json";
|
||||
resultText = File.ReadAllText(environmentSpecificPath);
|
||||
expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented);
|
||||
resultText.ShouldBe(expectedText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute()
|
||||
{
|
||||
|
@ -9,24 +9,30 @@ namespace Ocelot.UnitTests.Configuration
|
||||
using Xunit;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Ocelot.Configuration.Repository;
|
||||
|
||||
public class DiskFileConfigurationRepositoryTests
|
||||
public class DiskFileConfigurationRepositoryTests : IDisposable
|
||||
{
|
||||
private readonly Mock<IHostingEnvironment> _hostingEnvironment = new Mock<IHostingEnvironment>();
|
||||
private readonly Mock<IHostingEnvironment> _hostingEnvironment;
|
||||
private IFileConfigurationRepository _repo;
|
||||
private string _configurationPath;
|
||||
private string _environmentSpecificPath;
|
||||
private string _ocelotJsonPath;
|
||||
private FileConfiguration _result;
|
||||
private FileConfiguration _fileConfiguration;
|
||||
|
||||
// This is a bit dirty and it is dev.dev so that the ConfigurationBuilderExtensionsTests
|
||||
// cant pick it up if they run in parralel..sigh these are not really unit
|
||||
// tests but whatever...
|
||||
// cant pick it up if they run in parralel..and the semaphore stops them running at the same time...sigh
|
||||
// these are not really unit tests but whatever...
|
||||
private string _environmentName = "DEV.DEV";
|
||||
private static SemaphoreSlim _semaphore;
|
||||
|
||||
public DiskFileConfigurationRepositoryTests()
|
||||
{
|
||||
_semaphore = new SemaphoreSlim(1, 1);
|
||||
_semaphore.Wait();
|
||||
_hostingEnvironment = new Mock<IHostingEnvironment>();
|
||||
_hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName);
|
||||
_repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object);
|
||||
}
|
||||
@ -79,6 +85,33 @@ namespace Ocelot.UnitTests.Configuration
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_environment_file_configuration_and_ocelot_file_configuration()
|
||||
{
|
||||
var config = FakeFileConfigurationForSet();
|
||||
|
||||
this.Given(_ => GivenIHaveAConfiguration(config))
|
||||
.And(_ => GivenTheConfigurationIs(config))
|
||||
.And(_ => GivenTheUserAddedOcelotJson())
|
||||
.When(_ => WhenISetTheConfiguration())
|
||||
.Then(_ => ThenTheConfigurationIsStoredAs(config))
|
||||
.And(_ => ThenTheConfigurationJsonIsIndented(config))
|
||||
.Then(_ => ThenTheOcelotJsonIsStoredAs(config))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheUserAddedOcelotJson()
|
||||
{
|
||||
_ocelotJsonPath = $"{AppContext.BaseDirectory}/ocelot.json";
|
||||
|
||||
if (File.Exists(_ocelotJsonPath))
|
||||
{
|
||||
File.Delete(_ocelotJsonPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(_ocelotJsonPath, "Doesnt matter");
|
||||
}
|
||||
|
||||
private void GivenTheEnvironmentNameIsUnavailable()
|
||||
{
|
||||
_environmentName = null;
|
||||
@ -119,25 +152,32 @@ namespace Ocelot.UnitTests.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
private void ThenTheOcelotJsonIsStoredAs(FileConfiguration expecteds)
|
||||
{
|
||||
var resultText = File.ReadAllText(_ocelotJsonPath);
|
||||
var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented);
|
||||
resultText.ShouldBe(expectedText);
|
||||
}
|
||||
|
||||
private void GivenTheConfigurationIs(FileConfiguration fileConfiguration)
|
||||
{
|
||||
_configurationPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
|
||||
_environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
|
||||
|
||||
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented);
|
||||
|
||||
if (File.Exists(_configurationPath))
|
||||
if (File.Exists(_environmentSpecificPath))
|
||||
{
|
||||
File.Delete(_configurationPath);
|
||||
File.Delete(_environmentSpecificPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(_configurationPath, jsonConfiguration);
|
||||
File.WriteAllText(_environmentSpecificPath, jsonConfiguration);
|
||||
}
|
||||
|
||||
private void ThenTheConfigurationJsonIsIndented(FileConfiguration expecteds)
|
||||
{
|
||||
var path = !string.IsNullOrEmpty(_configurationPath) ? _configurationPath : _configurationPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
|
||||
var path = !string.IsNullOrEmpty(_environmentSpecificPath) ? _environmentSpecificPath : _environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
|
||||
|
||||
var resultText = File.ReadAllText(_configurationPath);
|
||||
var resultText = File.ReadAllText(path);
|
||||
var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented);
|
||||
resultText.ShouldBe(expectedText);
|
||||
}
|
||||
@ -238,5 +278,10 @@ namespace Ocelot.UnitTests.Configuration
|
||||
ReRoutes = reRoutes
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user