diff --git a/README.md b/README.md index 56665fc8..4434b56f 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,12 @@ advice on the easiest way to do things :) Finally we mark all existing issues as help wanted, small, medium and large effort. If you want to contriute for the first time I suggest looking at a help wanted & small effort issue :) +## Donate + +If you think this project is worth supporting financially please make a contribution using the button below! + +[![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/ThreeMammals/) + ## Things that are currently annoying me [![](https://codescene.io/projects/697/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results) diff --git a/src/Ocelot/Configuration/FileConfigurationController.cs b/src/Ocelot/Configuration/FileConfigurationController.cs index 1ea7fa7d..dbc77b77 100644 --- a/src/Ocelot/Configuration/FileConfigurationController.cs +++ b/src/Ocelot/Configuration/FileConfigurationController.cs @@ -5,10 +5,10 @@ using Microsoft.AspNetCore.Mvc; using Ocelot.Configuration.File; using Ocelot.Configuration.Setter; using Ocelot.Raft; -using Rafty.Concensus; namespace Ocelot.Configuration { + using Rafty.Concensus.Node; using Repository; [Authorize] @@ -50,7 +50,7 @@ namespace Ocelot.Configuration { var node = (INode)test; var result = await node.Accept(new UpdateFileConfiguration(fileConfiguration)); - if (result.GetType() == typeof(Rafty.Concensus.ErrorResponse)) + if (result.GetType() == typeof(Rafty.Infrastructure.ErrorResponse)) { return new BadRequestObjectResult("There was a problem. This error message sucks raise an issue in GitHub."); } diff --git a/src/Ocelot/DependencyInjection/OcelotAdministrationBuilder.cs b/src/Ocelot/DependencyInjection/OcelotAdministrationBuilder.cs index 580839c3..c96cdef3 100644 --- a/src/Ocelot/DependencyInjection/OcelotAdministrationBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotAdministrationBuilder.cs @@ -8,6 +8,8 @@ using Rafty.Log; namespace Ocelot.DependencyInjection { + using Rafty.Concensus.Node; + public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder { private readonly IServiceCollection _services; @@ -21,7 +23,7 @@ namespace Ocelot.DependencyInjection public IOcelotAdministrationBuilder AddRafty() { - var settings = new InMemorySettings(4000, 5000, 100, 5000); + var settings = new InMemorySettings(4000, 6000, 100, 10000); _services.AddSingleton(); _services.AddSingleton(); _services.AddSingleton(settings); diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 4fc3db7a..a35a8e1a 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -18,6 +18,7 @@ using Rafty.Infrastructure; using Ocelot.Middleware.Pipeline; using Pivotal.Discovery.Client; + using Rafty.Concensus.Node; public static class OcelotMiddlewareExtensions { @@ -91,7 +92,7 @@ applicationLifetime.ApplicationStopping.Register(() => OnShutdown(builder)); var node = (INode)builder.ApplicationServices.GetService(typeof(INode)); var nodeId = (NodeId)builder.ApplicationServices.GetService(typeof(NodeId)); - node.Start(nodeId.Id); + node.Start(nodeId); } private static async Task CreateConfiguration(IApplicationBuilder builder) diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index 01241f59..8224be2c 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -25,37 +25,37 @@ True - + NU1701 - - - - - - - - - + + + + + + + + + NU1701 - - - - + + + + all - - - - - - - - - + + + + + + + + + diff --git a/src/Ocelot/Raft/FileFsm.cs b/src/Ocelot/Raft/FileFsm.cs deleted file mode 100644 index afa47d26..00000000 --- a/src/Ocelot/Raft/FileFsm.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Newtonsoft.Json; -using Rafty.FiniteStateMachine; -using Rafty.Infrastructure; -using Rafty.Log; - -namespace Ocelot.Raft -{ - [ExcludeFromCoverage] - public class FileFsm : IFiniteStateMachine - { - private string _id; - - public FileFsm(NodeId nodeId) - { - _id = nodeId.Id.Replace("/","").Replace(":",""); - } - - public Task Handle(LogEntry log) - { - try - { - var json = JsonConvert.SerializeObject(log.CommandData); - File.AppendAllText(_id, json); - } - catch(Exception exception) - { - Console.WriteLine(exception); - } - - return Task.CompletedTask; - } - } -} diff --git a/src/Ocelot/Raft/FilePeersProvider.cs b/src/Ocelot/Raft/FilePeersProvider.cs index 52b877df..58edae9f 100644 --- a/src/Ocelot/Raft/FilePeersProvider.cs +++ b/src/Ocelot/Raft/FilePeersProvider.cs @@ -9,6 +9,8 @@ using Rafty.Infrastructure; namespace Ocelot.Raft { + using Rafty.Concensus.Peers; + [ExcludeFromCoverage] public class FilePeersProvider : IPeersProvider { diff --git a/src/Ocelot/Raft/HttpPeer.cs b/src/Ocelot/Raft/HttpPeer.cs index 93d81cd4..639f1ee8 100644 --- a/src/Ocelot/Raft/HttpPeer.cs +++ b/src/Ocelot/Raft/HttpPeer.cs @@ -10,6 +10,10 @@ using Rafty.FiniteStateMachine; namespace Ocelot.Raft { + using Rafty.Concensus.Messages; + using Rafty.Concensus.Peers; + using Rafty.Infrastructure; + [ExcludeFromCoverage] public class HttpPeer : IPeer { diff --git a/src/Ocelot/Raft/OcelotFiniteStateMachine.cs b/src/Ocelot/Raft/OcelotFiniteStateMachine.cs index b3d7720f..618d7f5f 100644 --- a/src/Ocelot/Raft/OcelotFiniteStateMachine.cs +++ b/src/Ocelot/Raft/OcelotFiniteStateMachine.cs @@ -8,7 +8,7 @@ namespace Ocelot.Raft [ExcludeFromCoverage] public class OcelotFiniteStateMachine : IFiniteStateMachine { - private IFileConfigurationSetter _setter; + private readonly IFileConfigurationSetter _setter; public OcelotFiniteStateMachine(IFileConfigurationSetter setter) { diff --git a/src/Ocelot/Raft/RaftController.cs b/src/Ocelot/Raft/RaftController.cs index 0b8d4989..660449c6 100644 --- a/src/Ocelot/Raft/RaftController.cs +++ b/src/Ocelot/Raft/RaftController.cs @@ -14,6 +14,9 @@ using Rafty.FiniteStateMachine; namespace Ocelot.Raft { + using Rafty.Concensus.Messages; + using Rafty.Concensus.Node; + [ExcludeFromCoverage] [Authorize] [Route("raft")] diff --git a/src/Ocelot/Raft/SqlLiteLog.cs b/src/Ocelot/Raft/SqlLiteLog.cs index f0db2047..882df202 100644 --- a/src/Ocelot/Raft/SqlLiteLog.cs +++ b/src/Ocelot/Raft/SqlLiteLog.cs @@ -1,286 +1,321 @@ -using System.IO; -using Rafty.Log; -using Microsoft.Data.Sqlite; -using Newtonsoft.Json; -using System; -using Rafty.Infrastructure; -using System.Collections.Generic; -using System.Threading.Tasks; - namespace Ocelot.Raft -{ - //todo - use async await - [ExcludeFromCoverage] +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Data.Sqlite; + using Microsoft.Extensions.Logging; + using Newtonsoft.Json; + using Rafty.Infrastructure; + using Rafty.Log; + public class SqlLiteLog : ILog { - private string _path; - private readonly object _lock = new object(); + private readonly string _path; + private readonly SemaphoreSlim _sempaphore = new SemaphoreSlim(1, 1); + private readonly ILogger _logger; + private readonly NodeId _nodeId; - public SqlLiteLog(NodeId nodeId) + public SqlLiteLog(NodeId nodeId, ILoggerFactory loggerFactory) { - _path = $"{nodeId.Id.Replace("/","").Replace(":","")}.db"; - if(!File.Exists(_path)) - { - lock(_lock) - { - FileStream fs = File.Create(_path); - fs.Dispose(); - } + _logger = loggerFactory.CreateLogger(); + _nodeId = nodeId; + _path = $"{nodeId.Id.Replace("/", "").Replace(":", "")}.db"; + _sempaphore.Wait(); - using(var connection = new SqliteConnection($"Data Source={_path};")) + if (!File.Exists(_path)) + { + var fs = File.Create(_path); + + fs.Dispose(); + + using (var connection = new SqliteConnection($"Data Source={_path};")) { connection.Open(); - var sql = @"create table logs ( + + const string sql = @"create table logs ( id integer primary key, data text not null )"; - using(var command = new SqliteCommand(sql, connection)) + + using (var command = new SqliteCommand(sql, connection)) { var result = command.ExecuteNonQuery(); + + _logger.LogInformation(result == 0 + ? $"id: {_nodeId.Id} create database, result: {result}" + : $"id: {_nodeId.Id} did not create database., result: {result}"); } } } + + _sempaphore.Release(); } - public Task LastLogIndex() + public async Task LastLogIndex() { - lock(_lock) + _sempaphore.Wait(); + var result = 1; + using (var connection = new SqliteConnection($"Data Source={_path};")) { - var result = 1; - using(var connection = new SqliteConnection($"Data Source={_path};")) + connection.Open(); + var sql = @"select id from logs order by id desc limit 1"; + using (var command = new SqliteCommand(sql, connection)) { - connection.Open(); - var sql = @"select id from logs order by id desc limit 1"; - using(var command = new SqliteCommand(sql, connection)) + var index = Convert.ToInt32(await command.ExecuteScalarAsync()); + if (index > result) { - var index = Convert.ToInt32(command.ExecuteScalar()); - if(index > result) - { - result = index; - } + result = index; } } - - return Task.FromResult(result); } + + _sempaphore.Release(); + return result; } - public Task LastLogTerm () + public async Task LastLogTerm() { - lock(_lock) + _sempaphore.Wait(); + long result = 0; + using (var connection = new SqliteConnection($"Data Source={_path};")) { - long result = 0; - using(var connection = new SqliteConnection($"Data Source={_path};")) + connection.Open(); + var sql = @"select data from logs order by id desc limit 1"; + using (var command = new SqliteCommand(sql, connection)) { - connection.Open(); - var sql = @"select data from logs order by id desc limit 1"; - using(var command = new SqliteCommand(sql, connection)) - { - var data = Convert.ToString(command.ExecuteScalar()); - var jsonSerializerSettings = new JsonSerializerSettings() { - TypeNameHandling = TypeNameHandling.All - }; - var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); - if(log != null && log.Term > result) - { - result = log.Term; - } - } - } - - return Task.FromResult(result); - } - } - - public Task Count () - { - lock(_lock) - { - var result = 0; - using(var connection = new SqliteConnection($"Data Source={_path};")) - { - connection.Open(); - var sql = @"select count(id) from logs"; - using(var command = new SqliteCommand(sql, connection)) - { - var index = Convert.ToInt32(command.ExecuteScalar()); - if(index > result) - { - result = index; - } - } - } - - return Task.FromResult(result); - } - } - - public Task Apply(LogEntry log) - { - lock(_lock) - { - using(var connection = new SqliteConnection($"Data Source={_path};")) - { - connection.Open(); - var jsonSerializerSettings = new JsonSerializerSettings() { + var data = Convert.ToString(await command.ExecuteScalarAsync()); + var jsonSerializerSettings = new JsonSerializerSettings() + { TypeNameHandling = TypeNameHandling.All }; - var data = JsonConvert.SerializeObject(log, jsonSerializerSettings); - - //todo - sql injection dont copy this.. - var sql = $"insert into logs (data) values ('{data}')"; - using(var command = new SqliteCommand(sql, connection)) + var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); + if (log != null && log.Term > result) { - var result = command.ExecuteNonQuery(); + result = log.Term; } - - sql = "select last_insert_rowid()"; - using(var command = new SqliteCommand(sql, connection)) - { - var result = command.ExecuteScalar(); - return Task.FromResult(Convert.ToInt32(result)); - } } } + _sempaphore.Release(); + return result; + } + + public async Task Count() + { + _sempaphore.Wait(); + var result = 0; + using (var connection = new SqliteConnection($"Data Source={_path};")) + { + connection.Open(); + var sql = @"select count(id) from logs"; + using (var command = new SqliteCommand(sql, connection)) + { + var index = Convert.ToInt32(await command.ExecuteScalarAsync()); + if (index > result) + { + result = index; + } + } + } + _sempaphore.Release(); + return result; + } + + public async Task Apply(LogEntry log) + { + _sempaphore.Wait(); + using (var connection = new SqliteConnection($"Data Source={_path};")) + { + connection.Open(); + var jsonSerializerSettings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + var data = JsonConvert.SerializeObject(log, jsonSerializerSettings); + //todo - sql injection dont copy this.. + var sql = $"insert into logs (data) values ('{data}')"; + _logger.LogInformation($"id: {_nodeId.Id}, sql: {sql}"); + using (var command = new SqliteCommand(sql, connection)) + { + var result = await command.ExecuteNonQueryAsync(); + _logger.LogInformation($"id: {_nodeId.Id}, insert log result: {result}"); + } + + sql = "select last_insert_rowid()"; + using (var command = new SqliteCommand(sql, connection)) + { + var result = await command.ExecuteScalarAsync(); + _logger.LogInformation($"id: {_nodeId.Id}, about to release semaphore"); + _sempaphore.Release(); + _logger.LogInformation($"id: {_nodeId.Id}, saved log to sqlite"); + return Convert.ToInt32(result); + } + } } - public Task DeleteConflictsFromThisLog(int index, LogEntry logEntry) + public async Task DeleteConflictsFromThisLog(int index, LogEntry logEntry) { - lock(_lock) + _sempaphore.Wait(); + using (var connection = new SqliteConnection($"Data Source={_path};")) { - using(var connection = new SqliteConnection($"Data Source={_path};")) + connection.Open(); + //todo - sql injection dont copy this.. + var sql = $"select data from logs where id = {index};"; + _logger.LogInformation($"id: {_nodeId.Id} sql: {sql}"); + using (var command = new SqliteCommand(sql, connection)) { - connection.Open(); - - //todo - sql injection dont copy this.. - var sql = $"select data from logs where id = {index};"; - using(var command = new SqliteCommand(sql, connection)) + var data = Convert.ToString(await command.ExecuteScalarAsync()); + var jsonSerializerSettings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + + _logger.LogInformation($"id {_nodeId.Id} got log for index: {index}, data is {data} and new log term is {logEntry.Term}"); + + var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); + if (logEntry != null && log != null && logEntry.Term != log.Term) { - var data = Convert.ToString(command.ExecuteScalar()); - var jsonSerializerSettings = new JsonSerializerSettings() { - TypeNameHandling = TypeNameHandling.All - }; - var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); - if(logEntry != null && log != null && logEntry.Term != log.Term) + //todo - sql injection dont copy this.. + var deleteSql = $"delete from logs where id >= {index};"; + _logger.LogInformation($"id: {_nodeId.Id} sql: {deleteSql}"); + using (var deleteCommand = new SqliteCommand(deleteSql, connection)) { - //todo - sql injection dont copy this.. - var deleteSql = $"delete from logs where id >= {index};"; - using(var deleteCommand = new SqliteCommand(deleteSql, connection)) - { - var result = deleteCommand.ExecuteNonQuery(); - } + var result = await deleteCommand.ExecuteNonQueryAsync(); } } } } - - return Task.CompletedTask; + _sempaphore.Release(); } - public Task Get(int index) + public async Task IsDuplicate(int index, LogEntry logEntry) { - lock(_lock) + _sempaphore.Wait(); + using (var connection = new SqliteConnection($"Data Source={_path};")) { - using(var connection = new SqliteConnection($"Data Source={_path};")) + connection.Open(); + //todo - sql injection dont copy this.. + var sql = $"select data from logs where id = {index};"; + using (var command = new SqliteCommand(sql, connection)) { - connection.Open(); - - //todo - sql injection dont copy this.. - var sql = $"select data from logs where id = {index}"; - using(var command = new SqliteCommand(sql, connection)) + var data = Convert.ToString(await command.ExecuteScalarAsync()); + var jsonSerializerSettings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + + var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); + + if (logEntry != null && log != null && logEntry.Term == log.Term) { - var data = Convert.ToString(command.ExecuteScalar()); - var jsonSerializerSettings = new JsonSerializerSettings() { - TypeNameHandling = TypeNameHandling.All - }; - var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); - return Task.FromResult(log); + _sempaphore.Release(); + return true; } } } + + _sempaphore.Release(); + return false; + } + + public async Task Get(int index) + { + _sempaphore.Wait(); + using (var connection = new SqliteConnection($"Data Source={_path};")) + { + connection.Open(); + //todo - sql injection dont copy this.. + var sql = $"select data from logs where id = {index}"; + using (var command = new SqliteCommand(sql, connection)) + { + var data = Convert.ToString(await command.ExecuteScalarAsync()); + var jsonSerializerSettings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); + _sempaphore.Release(); + return log; + } + } } - public Task> GetFrom(int index) + public async Task> GetFrom(int index) { - lock(_lock) - { - var logsToReturn = new List<(int, LogEntry)>(); + _sempaphore.Wait(); + var logsToReturn = new List<(int, LogEntry)>(); - using(var connection = new SqliteConnection($"Data Source={_path};")) + using (var connection = new SqliteConnection($"Data Source={_path};")) + { + connection.Open(); + //todo - sql injection dont copy this.. + var sql = $"select id, data from logs where id >= {index}"; + using (var command = new SqliteCommand(sql, connection)) { - connection.Open(); - - //todo - sql injection dont copy this.. - var sql = $"select id, data from logs where id >= {index}"; - using(var command = new SqliteCommand(sql, connection)) + using (var reader = await command.ExecuteReaderAsync()) { - using(var reader = command.ExecuteReader()) + while (reader.Read()) { - while(reader.Read()) - { - var id = Convert.ToInt32(reader[0]); - var data = (string)reader[1]; - var jsonSerializerSettings = new JsonSerializerSettings() { - TypeNameHandling = TypeNameHandling.All - }; - var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); - logsToReturn.Add((id, log)); - } + var id = Convert.ToInt32(reader[0]); + var data = (string)reader[1]; + var jsonSerializerSettings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); + logsToReturn.Add((id, log)); + } } - } - - return Task.FromResult(logsToReturn); - } - } - - public Task GetTermAtIndex(int index) - { - lock(_lock) - { - long result = 0; - using(var connection = new SqliteConnection($"Data Source={_path};")) - { - connection.Open(); - - //todo - sql injection dont copy this.. - var sql = $"select data from logs where id = {index}"; - using(var command = new SqliteCommand(sql, connection)) - { - var data = Convert.ToString(command.ExecuteScalar()); - var jsonSerializerSettings = new JsonSerializerSettings() { - TypeNameHandling = TypeNameHandling.All - }; - var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); - if(log != null && log.Term > result) - { - result = log.Term; - } - } - } - - return Task.FromResult(result); + } + _sempaphore.Release(); + return logsToReturn; } } - public Task Remove(int indexOfCommand) + public async Task GetTermAtIndex(int index) { - lock(_lock) + _sempaphore.Wait(); + long result = 0; + using (var connection = new SqliteConnection($"Data Source={_path};")) { - using(var connection = new SqliteConnection($"Data Source={_path};")) + connection.Open(); + //todo - sql injection dont copy this.. + var sql = $"select data from logs where id = {index}"; + using (var command = new SqliteCommand(sql, connection)) { - connection.Open(); - - //todo - sql injection dont copy this.. - var deleteSql = $"delete from logs where id >= {indexOfCommand};"; - using(var deleteCommand = new SqliteCommand(deleteSql, connection)) + var data = Convert.ToString(await command.ExecuteScalarAsync()); + var jsonSerializerSettings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); + if (log != null && log.Term > result) { - var result = deleteCommand.ExecuteNonQuery(); + result = log.Term; } } } - - return Task.CompletedTask; + _sempaphore.Release(); + return result; + } + public async Task Remove(int indexOfCommand) + { + _sempaphore.Wait(); + using (var connection = new SqliteConnection($"Data Source={_path};")) + { + connection.Open(); + //todo - sql injection dont copy this.. + var deleteSql = $"delete from logs where id >= {indexOfCommand};"; + _logger.LogInformation($"id: {_nodeId.Id} Remove {deleteSql}"); + using (var deleteCommand = new SqliteCommand(deleteSql, connection)) + { + var result = await deleteCommand.ExecuteNonQueryAsync(); + } + } + _sempaphore.Release(); } } -} +} diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index fe4877ec..dd9d855c 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -1,60 +1,60 @@ - - - 0.0.0-dev - netcoreapp2.0 - 2.0.0 - Ocelot.AcceptanceTests - Exe - Ocelot.AcceptanceTests - true - osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 - false - false - false - ..\..\codeanalysis.ruleset - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - - - - - - - - - - all - - - - - - - - - - - - - - - - - - - + + + 0.0.0-dev + netcoreapp2.0 + 2.0.0 + Ocelot.AcceptanceTests + Exe + Ocelot.AcceptanceTests + true + osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 + false + false + false + ..\..\codeanalysis.ruleset + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + + + + all + + + + + + + + + + + + + + + + + + + diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 5ec67320..5b2cac21 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -1,50 +1,50 @@ - - - 0.0.0-dev - netcoreapp2.0 - 2.0.0 - Ocelot.IntegrationTests - Exe - Ocelot.IntegrationTests - true - win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 - false - false - false - ..\..\codeanalysis.ruleset - - - - PreserveNewest - - - - - - - - - - - - - all - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + 0.0.0-dev + netcoreapp2.0 + 2.0.0 + Ocelot.IntegrationTests + Exe + Ocelot.IntegrationTests + true + win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 + false + false + false + ..\..\codeanalysis.ruleset + + + + PreserveNewest + + + + + + + + + + + + + all + + + + + + + + + + + + + + + + + + + diff --git a/test/Ocelot.IntegrationTests/RaftTests.cs b/test/Ocelot.IntegrationTests/RaftTests.cs index 96cd6687..37a7dff0 100644 --- a/test/Ocelot.IntegrationTests/RaftTests.cs +++ b/test/Ocelot.IntegrationTests/RaftTests.cs @@ -23,6 +23,7 @@ using Ocelot.Middleware; namespace Ocelot.IntegrationTests { + using System.Threading.Tasks; using Xunit.Abstractions; public class RaftTests : IDisposable @@ -31,7 +32,7 @@ namespace Ocelot.IntegrationTests private readonly List _webHostBuilders; private readonly List _threads; private FilePeers _peers; - private readonly HttpClient _httpClient; + private HttpClient _httpClient; private readonly HttpClient _httpClientForAssertions; private BearerToken _token; private HttpResponseMessage _response; @@ -42,18 +43,28 @@ namespace Ocelot.IntegrationTests { _output = output; _httpClientForAssertions = new HttpClient(); - _httpClient = new HttpClient(); - var ocelotBaseUrl = "http://localhost:5000"; - _httpClient.BaseAddress = new Uri(ocelotBaseUrl); _webHostBuilders = new List(); _builders = new List(); _threads = new List(); } - [Fact(Skip = "still broken waiting for work in rafty")] - public void should_persist_command_to_five_servers() + [Fact] + public async Task should_persist_command_to_five_servers() { - var configuration = new FileConfiguration + var peers = new List + { + new FilePeer {HostAndPort = "http://localhost:5000"}, + + new FilePeer {HostAndPort = "http://localhost:5001"}, + + new FilePeer {HostAndPort = "http://localhost:5002"}, + + new FilePeer {HostAndPort = "http://localhost:5003"}, + + new FilePeer {HostAndPort = "http://localhost:5004"} + }; + + var configuration = new FileConfiguration { GlobalConfiguration = new FileGlobalConfiguration { @@ -101,20 +112,34 @@ namespace Ocelot.IntegrationTests }; var command = new UpdateFileConfiguration(updatedConfiguration); + GivenThePeersAre(peers); GivenThereIsAConfiguration(configuration); GivenFiveServersAreRunning(); - GivenIHaveAnOcelotToken("/administration"); - WhenISendACommandIntoTheCluster(command); + await GivenIHaveAnOcelotToken("/administration"); + await WhenISendACommandIntoTheCluster(command); Thread.Sleep(5000); - ThenTheCommandIsReplicatedToAllStateMachines(command); + await ThenTheCommandIsReplicatedToAllStateMachines(command); } - [Fact(Skip = "still broken waiting for work in rafty")] - public void should_persist_command_to_five_servers_when_using_administration_api() + [Fact] + public async Task should_persist_command_to_five_servers_when_using_administration_api() { - var configuration = new FileConfiguration - { - }; + var peers = new List + { + new FilePeer {HostAndPort = "http://localhost:5005"}, + + new FilePeer {HostAndPort = "http://localhost:5006"}, + + new FilePeer {HostAndPort = "http://localhost:5007"}, + + new FilePeer {HostAndPort = "http://localhost:5008"}, + + new FilePeer {HostAndPort = "http://localhost:5009"} + }; + + var configuration = new FileConfiguration + { + }; var updatedConfiguration = new FileConfiguration { @@ -154,17 +179,29 @@ namespace Ocelot.IntegrationTests }; var command = new UpdateFileConfiguration(updatedConfiguration); + GivenThePeersAre(peers); GivenThereIsAConfiguration(configuration); GivenFiveServersAreRunning(); - GivenIHaveAnOcelotToken("/administration"); + await GivenIHaveAnOcelotToken("/administration"); GivenIHaveAddedATokenToMyRequest(); - WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration); - ThenTheCommandIsReplicatedToAllStateMachines(command); + await WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration); + await ThenTheCommandIsReplicatedToAllStateMachines(command); } - private void WhenISendACommandIntoTheCluster(UpdateFileConfiguration command) + private void GivenThePeersAre(List peers) { - bool SendCommand() + FilePeers filePeers = new FilePeers(); + filePeers.Peers.AddRange(peers); + var json = JsonConvert.SerializeObject(filePeers); + File.WriteAllText("peers.json", json); + _httpClient = new HttpClient(); + var ocelotBaseUrl = peers[0].HostAndPort; + _httpClient.BaseAddress = new Uri(ocelotBaseUrl); + } + + private async Task WhenISendACommandIntoTheCluster(UpdateFileConfiguration command) + { + async Task SendCommand() { try { @@ -174,13 +211,13 @@ namespace Ocelot.IntegrationTests TypeNameHandling = TypeNameHandling.All }); var httpContent = new StringContent(json); - httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); using (var httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - var response = httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent).GetAwaiter().GetResult(); + var response = await httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent); response.EnsureSuccessStatusCode(); - var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + var content = await response.Content.ReadAsStringAsync(); var errorResult = JsonConvert.DeserializeObject>(content); @@ -206,13 +243,19 @@ namespace Ocelot.IntegrationTests } } - var commandSent = WaitFor(20000).Until(() => SendCommand()); + var commandSent = await WaitFor(40000).Until(async () => + { + var result = await SendCommand(); + Thread.Sleep(1000); + return result; + }); + commandSent.ShouldBeTrue(); } - private void ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds) + private async Task ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds) { - bool CommandCalledOnAllStateMachines() + async Task CommandCalledOnAllStateMachines() { try { @@ -232,8 +275,8 @@ namespace Ocelot.IntegrationTests } _httpClientForAssertions.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - var result = _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration").Result; - var json = result.Content.ReadAsStringAsync().Result; + var result = await _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration"); + var json = await result.Content.ReadAsStringAsync(); var response = JsonConvert.DeserializeObject(json, new JsonSerializerSettings{TypeNameHandling = TypeNameHandling.All}); response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.Configuration.GlobalConfiguration.RequestIdKey); response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host); @@ -268,19 +311,29 @@ namespace Ocelot.IntegrationTests } } - var commandOnAllStateMachines = WaitFor(20000).Until(() => CommandCalledOnAllStateMachines()); + var commandOnAllStateMachines = await WaitFor(40000).Until(async () => + { + var result = await CommandCalledOnAllStateMachines(); + Thread.Sleep(1000); + return result; + }); + commandOnAllStateMachines.ShouldBeTrue(); } - private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) + private async Task WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) { - bool SendCommand() + async Task SendCommand() { var json = JsonConvert.SerializeObject(updatedConfiguration); + var content = new StringContent(json); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - _response = _httpClient.PostAsync(url, content).Result; - var responseContent = _response.Content.ReadAsStringAsync().Result; + + _response = await _httpClient.PostAsync(url, content); + + var responseContent = await _response.Content.ReadAsStringAsync(); if(responseContent == "There was a problem. This error message sucks raise an issue in GitHub.") { @@ -295,8 +348,14 @@ namespace Ocelot.IntegrationTests return _response.IsSuccessStatusCode; } - var commandSent = WaitFor(20000).Until(() => SendCommand()); - commandSent.ShouldBeTrue(); + var commandSent = await WaitFor(40000).Until(async () => + { + var result = await SendCommand(); + Thread.Sleep(1000); + return result; + }); + + commandSent.ShouldBeTrue(); } private void GivenIHaveAddedATokenToMyRequest() @@ -304,9 +363,9 @@ namespace Ocelot.IntegrationTests _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); } - private void GivenIHaveAnOcelotToken(string adminPath) + private async Task GivenIHaveAnOcelotToken(string adminPath) { - bool AddToken() + async Task AddToken() { try { @@ -320,8 +379,8 @@ namespace Ocelot.IntegrationTests }; var content = new FormUrlEncodedContent(formData); - var response = _httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; + var response = await _httpClient.PostAsync(tokenUrl, content); + var responseContent = await response.Content.ReadAsStringAsync(); if(!response.IsSuccessStatusCode) { return false; @@ -329,7 +388,7 @@ namespace Ocelot.IntegrationTests _token = JsonConvert.DeserializeObject(responseContent); var configPath = $"{adminPath}/.well-known/openid-configuration"; - response = _httpClient.GetAsync(configPath).Result; + response = await _httpClient.GetAsync(configPath); return response.IsSuccessStatusCode; } catch(Exception) @@ -338,7 +397,13 @@ namespace Ocelot.IntegrationTests } } - var addToken = WaitFor(20000).Until(() => AddToken()); + var addToken = await WaitFor(40000).Until(async () => + { + var result = await AddToken(); + Thread.Sleep(1000); + return result; + }); + addToken.ShouldBeTrue(); } diff --git a/test/Ocelot.UnitTests/Controllers/FileConfigurationControllerTests.cs b/test/Ocelot.UnitTests/Controllers/FileConfigurationControllerTests.cs index 41cc3573..8432d584 100644 --- a/test/Ocelot.UnitTests/Controllers/FileConfigurationControllerTests.cs +++ b/test/Ocelot.UnitTests/Controllers/FileConfigurationControllerTests.cs @@ -15,6 +15,7 @@ using Ocelot.Configuration; namespace Ocelot.UnitTests.Controllers { using Ocelot.Configuration.Repository; + using Rafty.Concensus.Node; public class FileConfigurationControllerTests { @@ -126,14 +127,14 @@ namespace Ocelot.UnitTests.Controllers { _node .Setup(x => x.Accept(It.IsAny())) - .ReturnsAsync(new Rafty.Concensus.OkResponse(new UpdateFileConfiguration(new FileConfiguration()))); + .ReturnsAsync(new Rafty.Infrastructure.OkResponse(new UpdateFileConfiguration(new FileConfiguration()))); } private void GivenTheNodeReturnsError() { _node .Setup(x => x.Accept(It.IsAny())) - .ReturnsAsync(new Rafty.Concensus.ErrorResponse("error", new UpdateFileConfiguration(new FileConfiguration()))); + .ReturnsAsync(new Rafty.Infrastructure.ErrorResponse("error", new UpdateFileConfiguration(new FileConfiguration()))); } private void GivenTheConfigSetterReturns(Response response) diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 9aa7db12..bfef63e0 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -1,58 +1,58 @@ - - - - 0.0.0-dev - netcoreapp2.0 - 2.0.0 - Ocelot.UnitTests - Ocelot.UnitTests - Exe - true - osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 - false - false - false - ..\..\codeanalysis.ruleset - - - - full - True - - - - - - - - - - - - - - - - all - - - - - - - - - - - - - - - - - - - - - - + + + + 0.0.0-dev + netcoreapp2.0 + 2.0.0 + Ocelot.UnitTests + Ocelot.UnitTests + Exe + true + osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 + false + false + false + ..\..\codeanalysis.ruleset + + + + full + True + + + + + + + + + + + + + + + + all + + + + + + + + + + + + + + + + + + + + + +