Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* ThreeMammals#463 save both files

* ThreeMammals#463 made it so we dont save to disk on startup unless using admin api
  • Loading branch information
TomPallister authored Jul 27, 2018
1 parent 9f44483 commit 1817564
Show file tree
Hide file tree
Showing 8 changed files with 484 additions and 274 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*.user
*.userosscache
*.sln.docstates

.DS_Store
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

Expand Down
6 changes: 4 additions & 2 deletions docs/features/administration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@ 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.
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}**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -26,7 +27,7 @@ public Task<Response<FileConfiguration>> Get()

lock(_lock)
{
jsonConfiguration = System.IO.File.ReadAllText(_configFilePath);
jsonConfiguration = System.IO.File.ReadAllText(_environmentFilePath);
}

var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(jsonConfiguration);
Expand All @@ -40,12 +41,19 @@ public Task<Response> Set(FileConfiguration fileConfiguration)

lock(_lock)
{
if (System.IO.File.Exists(_configFilePath))
if (System.IO.File.Exists(_environmentFilePath))
{
System.IO.File.Delete(_environmentFilePath);
}

System.IO.File.WriteAllText(_environmentFilePath, jsonConfiguration);

if (System.IO.File.Exists(_ocelotFilePath))
{
System.IO.File.Delete(_configFilePath);
System.IO.File.Delete(_ocelotFilePath);
}

System.IO.File.WriteAllText(_configFilePath, jsonConfiguration);
System.IO.File.WriteAllText(_ocelotFilePath, jsonConfiguration);
}

return Task.FromResult<Response>(new OkResponse());
Expand Down
52 changes: 31 additions & 21 deletions src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,18 +28,18 @@ public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder
await builder.UseOcelot(new OcelotPipelineConfiguration());

return builder;
}

}

public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, Action<OcelotPipelineConfiguration> pipelineConfiguration)
{
var config = new OcelotPipelineConfiguration();
pipelineConfiguration?.Invoke(config);
return await builder.UseOcelot(config);
}
{
var config = new OcelotPipelineConfiguration();
pipelineConfiguration?.Invoke(config);
return await builder.UseOcelot(config);
}
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
{
var configuration = await CreateConfiguration(builder);

var configuration = await CreateConfiguration(builder);

CreateAdministrationArea(builder, configuration);

if (UsingRafty(builder))
Expand Down Expand Up @@ -105,32 +106,42 @@ private static async Task<IInternalConfiguration> CreateConfiguration(IApplicati
{
// make configuration from file system?
// earlier user needed to add ocelot files in startup configuration stuff, asp.net will map it to this
var fileConfig = (IOptions<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));

// now create the config
var fileConfig = (IOptions<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));

// now create the config
var internalConfigCreator = (IInternalConfigurationCreator)builder.ApplicationServices.GetService(typeof(IInternalConfigurationCreator));
var internalConfig = await internalConfigCreator.Create(fileConfig.Value);

// now save it in memory
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)
Expand Down Expand Up @@ -179,8 +190,7 @@ private static async Task SetFileConfigInConsul(IApplicationBuilder builder,

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))
{
Expand Down Expand Up @@ -245,8 +255,8 @@ private static void ConfigureDiagnosticListener(IApplicationBuilder builder)
var listener = (OcelotDiagnosticListener)builder.ApplicationServices.GetService(typeof(OcelotDiagnosticListener));
var diagnosticListener = (DiagnosticListener)builder.ApplicationServices.GetService(typeof(DiagnosticListener));
diagnosticListener.SubscribeWithAdapter(listener);
}

}

private static void OnShutdown(IApplicationBuilder app)
{
var node = (INode)app.ApplicationServices.GetService(typeof(INode));
Expand Down
100 changes: 100 additions & 0 deletions test/Ocelot.AcceptanceTests/StartupTests.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 31 additions & 0 deletions test/Ocelot.AcceptanceTests/Steps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

namespace Ocelot.AcceptanceTests
{
using Configuration.Repository;
using Microsoft.Net.Http.Headers;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;

Expand Down Expand Up @@ -928,5 +929,35 @@ private async Task Fire(string url, string expectedBody, Random random)
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();

}
}
}
14 changes: 14 additions & 0 deletions test/Ocelot.IntegrationTests/AdministrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,23 @@ public void should_get_file_configuration_edit_and_post_updated_version()
.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()
{
Expand Down
Loading

0 comments on commit 1817564

Please sign in to comment.