#568 worked out how to check if qos handler present and kill Ocelot i… (#578)

* #568 worked out how to check if qos handler present and kill Ocelot if options specified but no handler, need to refactor this into fluent validation style

* #568 acceptance tests to make sure Ocelot won't start if the user specifies QoSOptions but doesnt have a QoSHandler registered
This commit is contained in:
Tom Pallister 2018-08-28 18:57:21 +01:00 committed by GitHub
parent 8db5570840
commit 29a7af9486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1694 additions and 1377 deletions

View File

@ -1,24 +1,30 @@
using System.Collections.Generic; using System.Collections.Generic;
using Ocelot.Errors; using Ocelot.Errors;
namespace Ocelot.Configuration.Validator namespace Ocelot.Configuration.Validator
{ {
public class ConfigurationValidationResult public class ConfigurationValidationResult
{ {
public ConfigurationValidationResult(bool isError) public ConfigurationValidationResult(bool isError)
{ {
IsError = isError; IsError = isError;
Errors = new List<Error>(); Errors = new List<Error>();
} }
public ConfigurationValidationResult(bool isError, List<Error> errors) public ConfigurationValidationResult(bool isError, Error error)
{ {
IsError = isError; IsError = isError;
Errors = errors; Errors = new List<Error> { error };
} }
public bool IsError { get; } public ConfigurationValidationResult(bool isError, List<Error> errors)
{
public List<Error> Errors { get; } IsError = isError;
} Errors = errors;
} }
public bool IsError { get; }
public List<Error> Errors { get; }
}
}

View File

@ -1,129 +1,133 @@
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Responses; using Ocelot.Responses;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ocelot.Configuration.Validator namespace Ocelot.Configuration.Validator
{ {
using System; using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Requester; using Requester;
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
{ {
private readonly IServiceProvider _provider; private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider) public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider)
{ {
_provider = provider; _qosDelegatingHandlerDelegate = provider.GetService<QosDelegatingHandlerDelegate>();
RuleFor(configuration => configuration.ReRoutes)
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, provider)); RuleFor(configuration => configuration.ReRoutes)
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, _qosDelegatingHandlerDelegate));
RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes)) RuleFor(configuration => configuration.GlobalConfiguration)
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate"); .SetValidator(new FileGlobalConfigurationFluentValidator(_qosDelegatingHandlerDelegate));
RuleForEach(configuration => configuration.ReRoutes) RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates)) .Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate"); .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
RuleForEach(configuration => configuration.Aggregates) RuleForEach(configuration => configuration.ReRoutes)
.Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates)) .Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates))
.WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate"); .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate");
RuleForEach(configuration => configuration.Aggregates) RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes)) .Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates))
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property"); .WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate");
RuleForEach(configuration => configuration.Aggregates) RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes)) .Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes))
.WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates"); .WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property");
}
RuleForEach(configuration => configuration.Aggregates)
private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List<FileReRoute> reRoutes) .Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes))
{ .WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates");
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key)); }
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count; public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
} {
var validateResult = await ValidateAsync(configuration);
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
{ if (validateResult.IsValid)
var validateResult = await ValidateAsync(configuration); {
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false));
if (validateResult.IsValid) }
{
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false)); var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage));
}
var result = new ConfigurationValidationResult(true, errors.Cast<Error>().ToList());
var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage));
return new OkResponse<ConfigurationValidationResult>(result);
var result = new ConfigurationValidationResult(true, errors.Cast<Error>().ToList()); }
return new OkResponse<ConfigurationValidationResult>(result); private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List<FileReRoute> reRoutes)
} {
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
List<FileReRoute> reRoutes) return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
{ }
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey)); List<FileReRoute> reRoutes)
} {
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
private static bool IsNotDuplicateIn(FileReRoute reRoute,
List<FileReRoute> reRoutes) return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey));
{ }
var matchingReRoutes = reRoutes
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate private static bool IsNotDuplicateIn(FileReRoute reRoute,
&& (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null)) List<FileReRoute> reRoutes)
.ToList(); {
var matchingReRoutes = reRoutes
if(matchingReRoutes.Count == 1) .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
{ && (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null))
return true; .ToList();
}
if (matchingReRoutes.Count == 1)
var allowAllVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count == 0); {
return true;
var duplicateAllowAllVerbs = matchingReRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1; }
var specificVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count != 0); var allowAllVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count == 0);
var duplicateSpecificVerbs = matchingReRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any(); var duplicateAllowAllVerbs = matchingReRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1;
if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs)) var specificVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count != 0);
{
return false; var duplicateSpecificVerbs = matchingReRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any();
}
if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs))
return true; {
} return false;
}
private static bool IsNotDuplicateIn(FileReRoute reRoute,
List<FileAggregateReRoute> aggregateReRoutes) return true;
{ }
var duplicate = aggregateReRoutes
.Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate private static bool IsNotDuplicateIn(FileReRoute reRoute,
&& a.UpstreamHost == reRoute.UpstreamHost List<FileAggregateReRoute> aggregateReRoutes)
&& reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get")); {
var duplicate = aggregateReRoutes
return !duplicate; .Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
} && a.UpstreamHost == reRoute.UpstreamHost
&& reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get"));
private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute,
List<FileAggregateReRoute> aggregateReRoutes) return !duplicate;
{ }
var matchingReRoutes = aggregateReRoutes
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute,
&& r.UpstreamHost == reRoute.UpstreamHost) List<FileAggregateReRoute> aggregateReRoutes)
.ToList(); {
var matchingReRoutes = aggregateReRoutes
return matchingReRoutes.Count <= 1; .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
} && r.UpstreamHost == reRoute.UpstreamHost)
} .ToList();
}
return matchingReRoutes.Count <= 1;
}
}
}

View File

@ -0,0 +1,21 @@
using FluentValidation;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Validator
{
using Requester;
public class FileGlobalConfigurationFluentValidator : AbstractValidator<FileGlobalConfiguration>
{
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public FileGlobalConfigurationFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
{
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
RuleFor(configuration => configuration.QoSOptions)
.SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
}
}
}

View File

@ -0,0 +1,28 @@
using FluentValidation;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Validator
{
using Requester;
public class FileQoSOptionsFluentValidator : AbstractValidator<FileQoSOptions>
{
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public FileQoSOptionsFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
{
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
When(qosOptions => qosOptions.TimeoutValue > 0 && qosOptions.ExceptionsAllowedBeforeBreaking > 0, () => {
RuleFor(qosOptions => qosOptions)
.Must(HaveQosHandlerRegistered)
.WithMessage("Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?");
});
}
private bool HaveQosHandlerRegistered(FileQoSOptions arg)
{
return _qosDelegatingHandlerDelegate != null;
}
}
}

View File

@ -1,92 +1,95 @@
namespace Ocelot.Configuration.Validator namespace Ocelot.Configuration.Validator
{ {
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System; using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Requester; using Requester;
public class ReRouteFluentValidator : AbstractValidator<FileReRoute> public class ReRouteFluentValidator : AbstractValidator<FileReRoute>
{ {
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider; private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
private readonly IServiceProvider _serviceProvider; private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider serviceProvider) public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
{ {
_authenticationSchemeProvider = authenticationSchemeProvider; _authenticationSchemeProvider = authenticationSchemeProvider;
_serviceProvider = serviceProvider; _qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
RuleFor(reRoute => reRoute.DownstreamPathTemplate) RuleFor(reRoute => reRoute.QoSOptions)
.Must(path => path.StartsWith("/")) .SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
RuleFor(reRoute => reRoute.UpstreamPathTemplate) .Must(path => path.StartsWith("/"))
.Must(path => !path.Contains("//")) .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
RuleFor(reRoute => reRoute.DownstreamPathTemplate) .Must(path => !path.Contains("//"))
.Must(path => !path.Contains("//")) .WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
RuleFor(reRoute => reRoute.UpstreamPathTemplate) .Must(path => !path.Contains("//"))
.Must(path => path.StartsWith("/")) .WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
RuleFor(reRoute => reRoute.DownstreamPathTemplate) .Must(path => path.StartsWith("/"))
.Must(path => !path.Contains("https://") && !path.Contains("http://")) .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
RuleFor(reRoute => reRoute.UpstreamPathTemplate) .Must(path => !path.Contains("https://") && !path.Contains("http://"))
.Must(path => !path.Contains("https://") && !path.Contains("http://")) .WithMessage("{PropertyName} {PropertyValue} contains scheme");
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
RuleFor(reRoute => reRoute.RateLimitOptions) .Must(path => !path.Contains("https://") && !path.Contains("http://"))
.Must(IsValidPeriod) .WithMessage("{PropertyName} {PropertyValue} contains scheme");
.WithMessage("RateLimitOptions.Period does not contains (s,m,h,d)");
RuleFor(reRoute => reRoute.RateLimitOptions)
RuleFor(reRoute => reRoute.AuthenticationOptions) .Must(IsValidPeriod)
.MustAsync(IsSupportedAuthenticationProviders) .WithMessage("RateLimitOptions.Period does not contains (s,m,h,d)");
.WithMessage("{PropertyValue} is unsupported authentication provider");
RuleFor(reRoute => reRoute.AuthenticationOptions)
When(reRoute => reRoute.UseServiceDiscovery, () => { .MustAsync(IsSupportedAuthenticationProviders)
RuleFor(r => r.ServiceName).NotEmpty() .WithMessage("{PropertyValue} is unsupported authentication provider");
.WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
}); When(reRoute => reRoute.UseServiceDiscovery, () => {
RuleFor(r => r.ServiceName).NotEmpty()
When(reRoute => !reRoute.UseServiceDiscovery, () => { .WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty() });
.WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
}); When(reRoute => !reRoute.UseServiceDiscovery, () => {
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty()
When(reRoute => !reRoute.UseServiceDiscovery, () => { .WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
RuleFor(reRoute => reRoute.DownstreamHostAndPorts) });
.SetCollectionValidator(new HostAndPortValidator());
}); When(reRoute => !reRoute.UseServiceDiscovery, () => {
} RuleFor(reRoute => reRoute.DownstreamHostAndPorts)
.SetCollectionValidator(new HostAndPortValidator());
private async Task<bool> IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken) });
{ }
if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey))
{ private async Task<bool> IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken)
return true; {
} if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey))
{
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync(); return true;
}
var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList();
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey);
} var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList();
private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions) return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey);
{ }
string period = rateLimitOptions.Period;
private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions)
return !rateLimitOptions.EnableRateLimiting || period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d"); {
} string period = rateLimitOptions.Period;
}
} return !rateLimitOptions.EnableRateLimiting || period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d");
}
}
}

View File

@ -1,53 +1,148 @@
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Shouldly; using Shouldly;
using Xunit; using Xunit;
public class CannotStartOcelotTests : IDisposable public class CannotStartOcelotTests : IDisposable
{ {
private readonly Steps _steps; private readonly Steps _steps;
public CannotStartOcelotTests() public CannotStartOcelotTests()
{ {
_steps = new Steps(); _steps = new Steps();
} }
[Fact] [Fact]
public void should_throw_exception_if_cannot_start() public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_globally()
{ {
var invalidConfig = new FileConfiguration() var invalidConfig = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
UpstreamPathTemplate = "api", DownstreamPathTemplate = "/",
DownstreamPathTemplate = "test" DownstreamScheme = "http",
} DownstreamHostAndPorts = new List<FileHostAndPort>
} {
}; new FileHostAndPort
{
Exception exception = null; Host = "localhost",
_steps.GivenThereIsAConfiguration(invalidConfig); Port = 51878,
try }
{ },
_steps.GivenOcelotIsRunning(); UpstreamPathTemplate = "/laura",
} UpstreamHttpMethod = new List<string> { "Get" },
catch(Exception ex) Key = "Laura",
{ }
exception = ex; },
} GlobalConfiguration = new FileGlobalConfiguration
{
exception.ShouldNotBeNull(); QoSOptions = new FileQoSOptions
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Downstream Path Template test doesnt start with forward slash,Upstream Path Template api doesnt start with forward slash,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!)"); {
} TimeoutValue = 1,
ExceptionsAllowedBeforeBreaking = 1
public void Dispose() }
{ }
_steps.Dispose(); };
}
} Exception exception = null;
} _steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch (Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?)");
}
[Fact]
public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_for_re_route()
{
var invalidConfig = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51878,
}
},
UpstreamPathTemplate = "/laura",
UpstreamHttpMethod = new List<string> { "Get" },
Key = "Laura",
QoSOptions = new FileQoSOptions
{
TimeoutValue = 1,
ExceptionsAllowedBeforeBreaking = 1
}
}
}
};
Exception exception = null;
_steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch (Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?)");
}
[Fact]
public void should_throw_exception_if_cannot_start()
{
var invalidConfig = new FileConfiguration()
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "api",
DownstreamPathTemplate = "test"
}
}
};
Exception exception = null;
_steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch (Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Downstream Path Template test doesnt start with forward slash,Upstream Path Template api doesnt start with forward slash,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!)");
}
public void Dispose()
{
_steps.Dispose();
}
}
}