-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Chaos Engineering to inject random crashes into calls #114
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think of the "IChaosStrategy" name? It sounds better to me. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace Mockaco | ||
{ | ||
internal interface IStrategyChaos | ||
{ | ||
Task Response(HttpResponse httpResponse); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using System.Net; | ||
using System.Text; | ||
|
||
namespace Mockaco | ||
{ | ||
internal class StrategyChaosBehavior : IStrategyChaos | ||
{ | ||
public async Task Response(HttpResponse httpResponse) | ||
{ | ||
httpResponse.StatusCode = (int)HttpStatusCode.ServiceUnavailable; | ||
|
||
var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.ServiceUnavailable}"); | ||
|
||
await httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length, default); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The await is not necessary here, you can directly return the task |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using System.Net; | ||
using System.Text; | ||
|
||
namespace Mockaco | ||
{ | ||
internal class StrategyChaosException : IStrategyChaos | ||
{ | ||
public async Task Response(HttpResponse httpResponse) | ||
{ | ||
httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError; | ||
|
||
var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.InternalServerError}"); | ||
|
||
await httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length, default); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The await is not necessary here, you can directly return the task |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using System.Net; | ||
using System.Text; | ||
|
||
namespace Mockaco | ||
{ | ||
internal class StrategyChaosLatency : IStrategyChaos | ||
{ | ||
private readonly ILogger<StrategyChaosLatency> _logger; | ||
private readonly IOptions<ChaosOptions> _options; | ||
|
||
public StrategyChaosLatency(ILogger<StrategyChaosLatency> logger, | ||
IOptions<ChaosOptions> options) | ||
{ | ||
_logger = logger; | ||
_options = options; | ||
} | ||
public async Task Response(HttpResponse httpResponse) | ||
{ | ||
var responseDelay = new Random().Next(_options.Value.MinimumLatencyTime, _options.Value.MaximumLatencyTime); | ||
_logger.LogInformation($"Chaos Latency (ms): {responseDelay}"); | ||
await Task.Delay(responseDelay); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The await is not necessary here, you can directly return the task |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using System.Net; | ||
using System.Text; | ||
|
||
namespace Mockaco | ||
{ | ||
internal class StrategyChaosResult : IStrategyChaos | ||
{ | ||
public async Task Response(HttpResponse httpResponse) | ||
{ | ||
httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; | ||
|
||
var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.BadRequest}"); | ||
|
||
await httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length, default); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The await is not necessary here, you can directly return the task |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using System.Net; | ||
using System.Text; | ||
|
||
namespace Mockaco | ||
{ | ||
internal class StrategyChaosTimeout : IStrategyChaos | ||
{ | ||
private readonly IOptions<ChaosOptions> _options; | ||
|
||
public StrategyChaosTimeout(IOptions<ChaosOptions> options) { | ||
_options = options; | ||
} | ||
public async Task Response(HttpResponse httpResponse) | ||
{ | ||
await Task.Delay(_options.Value.TimeBeforeTimeout); | ||
|
||
httpResponse.StatusCode = (int)HttpStatusCode.RequestTimeout; | ||
|
||
var bodyBytes = Encoding.UTF8.GetBytes($"Error {httpResponse.StatusCode}: {HttpStatusCode.RequestTimeout}"); | ||
|
||
await httpResponse.Body.WriteAsync(bodyBytes, 0, bodyBytes.Length, default); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,9 @@ public static IServiceCollection AddMockaco(this IServiceCollection services) => | |
public static IServiceCollection AddMockaco(this IServiceCollection services, Action<MockacoOptions> config) => | ||
services | ||
.AddOptions<MockacoOptions>().Configure(config).Services | ||
.AddOptions<ChaosOptions>() | ||
.Configure<IOptions<MockacoOptions>>((options, parent) => options = parent.Value.Chaos) | ||
.Services | ||
.AddOptions<TemplateFileProviderOptions>() | ||
.Configure<IOptions<MockacoOptions>>((options, parent) => options = parent.Value.TemplateFileProvider) | ||
.Services | ||
|
@@ -28,13 +31,15 @@ private static IServiceCollection AddConfiguration(this IServiceCollection servi | |
services | ||
.AddOptions() | ||
.Configure<MockacoOptions>(config) | ||
.Configure<ChaosOptions>(config.GetSection("Chaos")) | ||
.Configure<TemplateFileProviderOptions>(config.GetSection("TemplateFileProvider")); | ||
|
||
private static IServiceCollection AddCommonServices(this IServiceCollection services) => | ||
services | ||
.AddMemoryCache() | ||
.AddHttpClient() | ||
.AddInternalServices() | ||
.AddChaosServices() | ||
.AddHostedService<MockProviderWarmUp>(); | ||
|
||
private static IServiceCollection AddInternalServices(this IServiceCollection services) => | ||
|
@@ -70,5 +75,14 @@ private static IServiceCollection AddInternalServices(this IServiceCollection se | |
.AddTransient<ITemplateTransformer, TemplateTransformer>() | ||
|
||
.AddTemplatesGenerating(); | ||
|
||
private static IServiceCollection AddChaosServices(this IServiceCollection services) => | ||
services | ||
.AddSingleton<IStrategyChaos, StrategyChaosBehavior>() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why Singleton? all strategies seems to be perfectly transient. |
||
.AddSingleton<IStrategyChaos, StrategyChaosException>() | ||
.AddSingleton<IStrategyChaos, StrategyChaosLatency>() | ||
.AddSingleton<IStrategyChaos, StrategyChaosResult>() | ||
.AddSingleton<IStrategyChaos, StrategyChaosTimeout>(); | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using System.Net; | ||
|
||
namespace Mockaco | ||
{ | ||
internal class ChaosMiddleware | ||
{ | ||
private readonly RequestDelegate _next; | ||
private readonly IEnumerable<IStrategyChaos> _strategies; | ||
private readonly ILogger<ChaosMiddleware> _logger; | ||
private readonly IOptions<ChaosOptions> _options; | ||
|
||
private List<int> ErrorList { get; set; } | ||
private int Counter { get; set; } | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove additional blank lines |
||
public ChaosMiddleware(RequestDelegate next, IEnumerable<IStrategyChaos> strategies, ILogger<ChaosMiddleware> logger, IOptions<ChaosOptions> options | ||
) | ||
{ | ||
_next = next; | ||
_strategies = strategies; | ||
_logger = logger; | ||
_options = options; | ||
} | ||
|
||
public async Task Invoke(HttpContext httpContext) | ||
{ | ||
if (_options.Value.Enabled) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inverting this if would reduce the nesting |
||
{ | ||
Counter++; | ||
if (Counter > 100) | ||
Counter = 1; | ||
|
||
if (Counter == 1) | ||
ErrorList = GenerateErrorList(_options.Value.ChaosRate); | ||
|
||
if (ErrorList.Contains(Counter)) | ||
{ | ||
var selected = _strategies.Random(); | ||
_logger.LogInformation($"Chaos: {selected?.ToString()}"); | ||
await selected.Response(httpContext.Response); | ||
} | ||
|
||
if (httpContext.Response.StatusCode != (int)HttpStatusCode.OK) | ||
return; | ||
|
||
} | ||
|
||
await _next(httpContext); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The await is not necessary here, you can directly return the task :) |
||
|
||
} | ||
|
||
private List<int> GenerateErrorList(int rate) | ||
{ | ||
int items = rate; | ||
var list = new List<int>(); | ||
while (list.Count() < items) | ||
{ | ||
var item = new Random().Next(1, 100); | ||
if (!list.Contains(item)) | ||
{ | ||
list.Add(item); | ||
} | ||
} | ||
return list.OrderBy(x => x).ToList(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System.Net; | ||
|
||
namespace Mockaco | ||
{ | ||
public class ChaosOptions | ||
{ | ||
public bool Enabled { get; set; } | ||
public int ChaosRate { get; set; } | ||
public int MinimumLatencyTime { get; set; } | ||
public int MaximumLatencyTime { get; set; } | ||
public int TimeBeforeTimeout { get; set; } | ||
|
||
public ChaosOptions() | ||
{ | ||
Enabled = true; | ||
ChaosRate = 10; | ||
MinimumLatencyTime = 500; | ||
MaximumLatencyTime = 3000; | ||
TimeBeforeTimeout = 10000; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this