From 708b575857e67f373940836c18d6a0df6cc212b5 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 20 Nov 2020 09:31:45 +0000 Subject: [PATCH 01/34] upgrade csproj to net5.0 --- .../AdministrationApi.csproj | 4 +- samples/OcelotBasic/OcelotBasic.csproj | 2 +- .../OcelotEureka/ApiGateway/ApiGateway.csproj | 2 +- .../DownstreamService.csproj | 2 +- samples/OcelotGraphQL/OcelotGraphQL.csproj | 4 +- .../OcelotKube/ApiGateway/ApiGateway.csproj | 2 +- .../DownstreamService.csproj | 2 +- .../OcelotOpenTracing.csproj | 2 +- .../OcelotApplicationApiGateway.csproj | 4 +- .../OcelotApplicationService.csproj | 4 +- .../Ocelot.Administration.csproj | 78 ++++++++--------- .../Ocelot.Cache.CacheManager.csproj | 78 ++++++++--------- .../Ocelot.Provider.Consul.csproj | 76 ++++++++-------- .../Ocelot.Provider.Eureka.csproj | 2 +- .../Ocelot.Provider.Kubernetes.csproj | 86 +++++++++---------- .../Ocelot.Provider.Polly.csproj | 76 ++++++++-------- .../Ocelot.Provider.Rafty.csproj | 2 +- .../Ocelot.Tracing.Butterfly.csproj | 2 +- .../Ocelot.Tracing.OpenTracing.csproj | 2 +- src/Ocelot/Ocelot.csproj | 2 +- .../Ocelot.AcceptanceTests.csproj | 4 +- .../Ocelot.Benchmarks.csproj | 4 +- .../Ocelot.IntegrationTests.csproj | 4 +- .../Ocelot.ManualTest.csproj | 4 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 4 +- 25 files changed, 226 insertions(+), 226 deletions(-) diff --git a/samples/AdministrationApi/AdministrationApi.csproj b/samples/AdministrationApi/AdministrationApi.csproj index 810fa13c8..7c5f80ecd 100644 --- a/samples/AdministrationApi/AdministrationApi.csproj +++ b/samples/AdministrationApi/AdministrationApi.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/samples/OcelotBasic/OcelotBasic.csproj b/samples/OcelotBasic/OcelotBasic.csproj index 26b7a4412..be6fd6dab 100644 --- a/samples/OcelotBasic/OcelotBasic.csproj +++ b/samples/OcelotBasic/OcelotBasic.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 diff --git a/samples/OcelotEureka/ApiGateway/ApiGateway.csproj b/samples/OcelotEureka/ApiGateway/ApiGateway.csproj index bc5318be1..f7f4c81e4 100644 --- a/samples/OcelotEureka/ApiGateway/ApiGateway.csproj +++ b/samples/OcelotEureka/ApiGateway/ApiGateway.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 diff --git a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj index 209fa6d21..67a861a8a 100644 --- a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj +++ b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 diff --git a/samples/OcelotGraphQL/OcelotGraphQL.csproj b/samples/OcelotGraphQL/OcelotGraphQL.csproj index df8f3ec71..271ae56fa 100644 --- a/samples/OcelotGraphQL/OcelotGraphQL.csproj +++ b/samples/OcelotGraphQL/OcelotGraphQL.csproj @@ -1,6 +1,6 @@ - netcoreapp3.1 + net5.0 @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/samples/OcelotKube/ApiGateway/ApiGateway.csproj b/samples/OcelotKube/ApiGateway/ApiGateway.csproj index 8cf74f8d1..e4c39f28f 100644 --- a/samples/OcelotKube/ApiGateway/ApiGateway.csproj +++ b/samples/OcelotKube/ApiGateway/ApiGateway.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 InProcess Linux diff --git a/samples/OcelotKube/DownstreamService/DownstreamService.csproj b/samples/OcelotKube/DownstreamService/DownstreamService.csproj index 3947a3de9..3dc2bee23 100644 --- a/samples/OcelotKube/DownstreamService/DownstreamService.csproj +++ b/samples/OcelotKube/DownstreamService/DownstreamService.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 InProcess Linux diff --git a/samples/OcelotOpenTracing/OcelotOpenTracing.csproj b/samples/OcelotOpenTracing/OcelotOpenTracing.csproj index 295fc461c..93279c7b0 100644 --- a/samples/OcelotOpenTracing/OcelotOpenTracing.csproj +++ b/samples/OcelotOpenTracing/OcelotOpenTracing.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj index 186b43f36..c2f7c8a1e 100644 --- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj +++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj @@ -2,7 +2,7 @@ Stateless Web Service for Stateful OcelotApplicationApiGateway App - netcoreapp3.1 + net5.0 OcelotApplicationApiGateway Exe OcelotApplicationApiGateway @@ -19,4 +19,4 @@ - \ No newline at end of file + diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj index 236cb1790..a286dd790 100644 --- a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj +++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj @@ -3,7 +3,7 @@ Stateless Service Application Exe - netcoreapp3.1 + net5.0 OcelotApplicationService OcelotApplicationService $(PackageTargetFallback) @@ -15,4 +15,4 @@ - \ No newline at end of file + diff --git a/src/Ocelot.Administration/Ocelot.Administration.csproj b/src/Ocelot.Administration/Ocelot.Administration.csproj index 88375a183..d44b4b862 100644 --- a/src/Ocelot.Administration/Ocelot.Administration.csproj +++ b/src/Ocelot.Administration/Ocelot.Administration.csproj @@ -1,39 +1,39 @@ - - - netcoreapp3.1 - true - Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it - Ocelot.Administration - 0.0.0-dev - Ocelot.Administration - Ocelot.Administration - API Gateway;.NET core - https://github.com/ThreeMammals/Ocelot.Administration - https://github.com/ThreeMammals/Ocelot.Administration - http://threemammals.com/images/ocelot_logo.png - win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 - false - false - True - false - Tom Pallister - ..\..\codeanalysis.ruleset - - - full - True - - - - - - - all - - - - - - - - + + + net5.0 + true + Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it + Ocelot.Administration + 0.0.0-dev + Ocelot.Administration + Ocelot.Administration + API Gateway;.NET core + https://github.com/ThreeMammals/Ocelot.Administration + https://github.com/ThreeMammals/Ocelot.Administration + http://threemammals.com/images/ocelot_logo.png + win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 + false + false + True + false + Tom Pallister + ..\..\codeanalysis.ruleset + + + full + True + + + + + + + all + + + + + + + + diff --git a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj index dcc97dd6f..43baa3329 100644 --- a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj +++ b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj @@ -1,39 +1,39 @@ - - - netcoreapp3.1 - true - Provides Ocelot extensions to use CacheManager.Net - Ocelot.Cache.CacheManager - 0.0.0-dev - Ocelot.Cache.CacheManager - Ocelot.Cache.CacheManager - API Gateway;.NET core - https://github.com/ThreeMammals/Ocelot.Cache.CacheManager - https://github.com/ThreeMammals/Ocelot.Cache.CacheManager - win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 - false - false - True - false - Tom Pallister - ..\..\codeanalysis.ruleset - - - full - True - - - - - - - all - - - - - - - - - + + + net5.0 + true + Provides Ocelot extensions to use CacheManager.Net + Ocelot.Cache.CacheManager + 0.0.0-dev + Ocelot.Cache.CacheManager + Ocelot.Cache.CacheManager + API Gateway;.NET core + https://github.com/ThreeMammals/Ocelot.Cache.CacheManager + https://github.com/ThreeMammals/Ocelot.Cache.CacheManager + win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 + false + false + True + false + Tom Pallister + ..\..\codeanalysis.ruleset + + + full + True + + + + + + + all + + + + + + + + + diff --git a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj index eaae221fe..88a6afc33 100644 --- a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj +++ b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj @@ -1,38 +1,38 @@ - - - netcoreapp3.1 - true - Provides Ocelot extensions to use Consul - Ocelot.Provider.Consul - 0.0.0-dev - Ocelot.Provider.Consul - Ocelot.Provider.Consul - API Gateway;.NET core - https://github.com/ThreeMammals/Ocelot.Provider.Consul - https://github.com/ThreeMammals/Ocelot.Provider.Consul - http://threemammals.com/images/ocelot_logo.png - win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 - false - false - True - false - Tom Pallister - ..\..\codeanalysis.ruleset - - - full - True - - - - - - - - all - - - - - - + + + net5.0 + true + Provides Ocelot extensions to use Consul + Ocelot.Provider.Consul + 0.0.0-dev + Ocelot.Provider.Consul + Ocelot.Provider.Consul + API Gateway;.NET core + https://github.com/ThreeMammals/Ocelot.Provider.Consul + https://github.com/ThreeMammals/Ocelot.Provider.Consul + http://threemammals.com/images/ocelot_logo.png + win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 + false + false + True + false + Tom Pallister + ..\..\codeanalysis.ruleset + + + full + True + + + + + + + + all + + + + + + diff --git a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj index e808bf931..8fa630305 100644 --- a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj +++ b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 true Provides Ocelot extensions to use Eureka Ocelot.Provider.Eureka diff --git a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj index 8f3428656..a2de9ea89 100644 --- a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj +++ b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj @@ -1,43 +1,43 @@ - - - - netcoreapp3.1 - true - Ocelot - Provides Ocelot extensions to use kubernetes - https://github.com/ThreeMammals/Ocelot - http://threemammals.com/images/ocelot_logo.png - - Ocelot.Provider.Kubernetes - Ocelot.Provider.Kubernetes - API Gateway;.NET core - win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 - false - false - true - false - 0.0.0-dev - geffzhang - - ..\..\codeanalysis.ruleset - - - - - - - - - - - - - - - - - - - - - + + + + net5.0 + true + Ocelot + Provides Ocelot extensions to use kubernetes + https://github.com/ThreeMammals/Ocelot + http://threemammals.com/images/ocelot_logo.png + + Ocelot.Provider.Kubernetes + Ocelot.Provider.Kubernetes + API Gateway;.NET core + win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 + false + false + true + false + 0.0.0-dev + geffzhang + + ..\..\codeanalysis.ruleset + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj index e082c6066..a9cceebe4 100644 --- a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj +++ b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj @@ -1,38 +1,38 @@ - - - netcoreapp3.1 - true - Provides Ocelot extensions to use Polly.NET - Ocelot.Provider.Polly - 0.0.0-dev - Ocelot.Provider.Polly - Ocelot.Provider.Polly - API Gateway;.NET core - https://github.com/ThreeMammals/Ocelot.Provider.Polly - https://github.com/ThreeMammals/Ocelot.Provider.Polly - http://threemammals.com/images/ocelot_logo.png - win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 - false - false - True - false - Tom Pallister - ..\..\codeanalysis.ruleset - - - full - True - - - - - - - all - - - - - - - + + + net5.0 + true + Provides Ocelot extensions to use Polly.NET + Ocelot.Provider.Polly + 0.0.0-dev + Ocelot.Provider.Polly + Ocelot.Provider.Polly + API Gateway;.NET core + https://github.com/ThreeMammals/Ocelot.Provider.Polly + https://github.com/ThreeMammals/Ocelot.Provider.Polly + http://threemammals.com/images/ocelot_logo.png + win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 + false + false + True + false + Tom Pallister + ..\..\codeanalysis.ruleset + + + full + True + + + + + + + all + + + + + + + diff --git a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj b/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj index 058fb0cfd..7bb03727d 100644 --- a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj +++ b/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 true Provides Ocelot extensions to use Rafty Ocelot.Provider.Rafty diff --git a/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj b/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj index f2401e024..1182a364c 100644 --- a/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj +++ b/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 true This package provides methods to integrate Butterfly tracing with Ocelot. Ocelot.Tracing.Butterfly diff --git a/src/Ocelot.Tracing.OpenTracing/Ocelot.Tracing.OpenTracing.csproj b/src/Ocelot.Tracing.OpenTracing/Ocelot.Tracing.OpenTracing.csproj index 9b6d5abae..e341adba2 100644 --- a/src/Ocelot.Tracing.OpenTracing/Ocelot.Tracing.OpenTracing.csproj +++ b/src/Ocelot.Tracing.OpenTracing/Ocelot.Tracing.OpenTracing.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 0.0.0-dev Kjell-Åke Gafvelin This package provides OpenTracing support to Ocelot. diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index 8f4f981d1..1481b83c8 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 true Ocelot is an API Gateway. The project is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. In particular I want easy integration with IdentityServer reference and bearer tokens. reference tokens. Ocelot is a bunch of middlewares in a specific order. Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features. Ocelot diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 4ddf06a3e..456d002ef 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -1,7 +1,7 @@ 0.0.0-dev - netcoreapp3.1 + net5.0 Ocelot.AcceptanceTests Exe Ocelot.AcceptanceTests @@ -74,4 +74,4 @@ - \ No newline at end of file + diff --git a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj index 0cbe43071..cd3c62b76 100644 --- a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj +++ b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj @@ -2,7 +2,7 @@ 0.0.0-dev - netcoreapp3.1 + net5.0 Ocelot.Benchmarks Exe Ocelot.Benchmarks @@ -27,4 +27,4 @@ - \ No newline at end of file + diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 59b45dc49..fb156952d 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -1,7 +1,7 @@  0.0.0-dev - netcoreapp3.1 + net5.0 Ocelot.IntegrationTests Exe Ocelot.IntegrationTests @@ -58,4 +58,4 @@ - \ No newline at end of file + diff --git a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj index 40e36ebcc..d4e02c673 100644 --- a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj +++ b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj @@ -1,7 +1,7 @@  0.0.0-dev - netcoreapp3.1 + net5.0 true Ocelot.ManualTest Exe @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index b2a031d49..ec5cf981c 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -2,7 +2,7 @@ 0.0.0-dev - netcoreapp3.1 + net5.0 Ocelot.UnitTests Ocelot.UnitTests Exe @@ -94,4 +94,4 @@ runtime; build; native; contentfiles; analyzers - \ No newline at end of file + From 7254d4d1fbb93cde9ada1eecb1e9dccc761a6c5d Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 20 Nov 2020 10:51:49 +0000 Subject: [PATCH 02/34] +semver: breaking upgrade base build image to net5.0 --- docker/Dockerfile.base | 18 +++++++++--------- docker/docker-build.sh | 13 +++++++------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 02d91f53a..42985ca8f 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -1,9 +1,9 @@ -# this is the dockerfile that create the ocelot build container -# build with the docker-build.sh file in this folder -FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build - -RUN apt install gnupg ca-certificates -RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF -RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list -RUN apt update -RUN apt-get -y install mono-devel \ No newline at end of file +# this is the dockerfile that create the ocelot build container +# build with the docker-build.sh file in this folder +FROM mcr.microsoft.com/dotnet/sdk:5.0-focal AS build + +RUN apt update +RUN apt install -y dirmngr gnupg apt-transport-https ca-certificates software-properties-common +RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF +RUN apt-add-repository 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' +RUN apt install -y mono-devel diff --git a/docker/docker-build.sh b/docker/docker-build.sh index 8bb4e2065..338a66b52 100755 --- a/docker/docker-build.sh +++ b/docker/docker-build.sh @@ -1,6 +1,7 @@ -# this script build the ocelot docker file -docker build -t mijitt0m/ocelot-build -f Dockerfile.base . -echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin -docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:0.0.1 -docker push mijitt0m/ocelot-build:latest -docker push mijitt0m/ocelot-build:0.0.1 +# this script build the ocelot docker file +version=0.0.2 +docker build -t mijitt0m/ocelot-build -f Dockerfile.base . +echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin +docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:$version +docker push mijitt0m/ocelot-build:latest +docker push mijitt0m/ocelot-build:$version From 1c6bfc33143f140053eb7d8e38fd6742c2eb8380 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 20 Nov 2020 11:57:34 +0000 Subject: [PATCH 03/34] add make and build tools to image --- docker/Dockerfile.base | 2 ++ docker/Dockerfile.build | 30 ++++++++++++++--------------- docker/Dockerfile.release | 40 +++++++++++++++++++-------------------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 42985ca8f..7b57dd23c 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -7,3 +7,5 @@ RUN apt install -y dirmngr gnupg apt-transport-https ca-certificates software-pr RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF RUN apt-add-repository 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' RUN apt install -y mono-devel +RUN apt install -y make +RUN apt install -y build-essential diff --git a/docker/Dockerfile.build b/docker/Dockerfile.build index 0478da417..057d0a9ba 100644 --- a/docker/Dockerfile.build +++ b/docker/Dockerfile.build @@ -1,15 +1,15 @@ -# call from ocelot repo root with -# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build . -FROM mijitt0m/ocelot-build:0.0.1 - -ARG OCELOT_COVERALLS_TOKEN - -ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN - -WORKDIR /src - -COPY ./. . - -RUN chmod u+x build.sh - -RUN make build \ No newline at end of file +# call from ocelot repo root with +# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build . +FROM mijitt0m/ocelot-build:0.0.2 + +ARG OCELOT_COVERALLS_TOKEN + +ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN + +WORKDIR /src + +COPY ./. . + +RUN chmod u+x build.sh + +RUN make build diff --git a/docker/Dockerfile.release b/docker/Dockerfile.release index 5d63816d2..1a7a4676b 100644 --- a/docker/Dockerfile.release +++ b/docker/Dockerfile.release @@ -1,20 +1,20 @@ -# call from ocelot repo root with -# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.release . - -FROM mijitt0m/ocelot-build:0.0.1 - -ARG OCELOT_COVERALLS_TOKEN -ARG OCELOT_NUTGET_API_KEY -ARG OCELOT_GITHUB_API_KEY - -ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -ENV OCELOT_NUTGET_API_KEY=$OCELOT_NUTGET_API_KEY -ENV OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY - -WORKDIR /src - -COPY ./. . - -RUN chmod u+x build.sh - -RUN make release \ No newline at end of file +# call from ocelot repo root with +# docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.release . + +FROM mijitt0m/ocelot-build:0.0.2 + +ARG OCELOT_COVERALLS_TOKEN +ARG OCELOT_NUTGET_API_KEY +ARG OCELOT_GITHUB_API_KEY + +ENV OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN +ENV OCELOT_NUTGET_API_KEY=$OCELOT_NUTGET_API_KEY +ENV OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY + +WORKDIR /src + +COPY ./. . + +RUN chmod u+x build.sh + +RUN make release From a9eec31aafea005f820221c5b5e8bb988c4ec109 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 20 Nov 2020 12:10:46 +0000 Subject: [PATCH 04/34] fix code broken after net5.0 upgrade --- test/Ocelot.AcceptanceTests/HeaderTests.cs | 49 +++++++++++++--------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs index f5d69d138..728d455ec 100644 --- a/test/Ocelot.AcceptanceTests/HeaderTests.cs +++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs @@ -24,8 +24,8 @@ public HeaderTests() [Fact] public void should_transform_upstream_header() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -65,8 +65,8 @@ public void should_transform_upstream_header() [Fact] public void should_transform_downstream_header() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -105,8 +105,8 @@ public void should_transform_downstream_header() [Fact] public void should_fix_issue_190() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -149,8 +149,8 @@ public void should_fix_issue_190() [Fact] public void should_fix_issue_205() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -193,8 +193,8 @@ public void should_fix_issue_205() [Fact] public void should_fix_issue_417() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -241,8 +241,8 @@ public void should_fix_issue_417() [Fact] public void request_should_reuse_cookies_with_cookie_container() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -284,8 +284,8 @@ public void request_should_reuse_cookies_with_cookie_container() [Fact] public void request_should_have_own_cookies_no_cookie_container() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -327,8 +327,8 @@ public void request_should_have_own_cookies_no_cookie_container() [Fact] public void issue_474_should_not_put_spaces_in_header() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -364,8 +364,8 @@ public void issue_474_should_not_put_spaces_in_header() [Fact] public void issue_474_should_put_spaces_in_header() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { @@ -413,9 +413,18 @@ private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int return Task.CompletedTask; } - if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue)) + if (context.Request.Cookies.TryGetValue("test", out var cookieValue)) + { + if (cookieValue == "0") + { + context.Response.StatusCode = statusCode; + return Task.CompletedTask; + } + } + + if (context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue)) { - if (cookieValue == "0" || headerValue == "test=1; path=/") + if (headerValue == "test=1; path=/") { context.Response.StatusCode = statusCode; return Task.CompletedTask; From 7f49bf5fe3bbd841c0aeef261a4ecf2c65a95c7a Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 20 Nov 2020 16:43:42 +0000 Subject: [PATCH 05/34] wip --- .../Eureka/EurekaMiddlewareConfigurationProviderTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs index 5a6a5306d..15054d822 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs @@ -25,7 +25,7 @@ public void should_not_build() services.AddSingleton(configRepo.Object); var sp = services.BuildServiceProvider(); var provider = EurekaMiddlewareConfigurationProvider.Get(new ApplicationBuilder(sp)); - provider.ShouldBeOfType(); + provider.GetType().BaseType.ShouldBeOfType(); } [Fact] @@ -41,7 +41,7 @@ public void should_build() services.AddSingleton(client.Object); var sp = services.BuildServiceProvider(); var provider = EurekaMiddlewareConfigurationProvider.Get(new ApplicationBuilder(sp)); - provider.ShouldBeOfType(); + provider.GetType().BaseType.ShouldBeOfType(); } } } From c1776fb59f5d57d8b92b0afb8039f30418b0ebe4 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 23 Nov 2020 11:19:54 +0000 Subject: [PATCH 06/34] fix warnings --- .../AuthenticationTests.cs | 636 +++++++++--------- 1 file changed, 318 insertions(+), 318 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index f019c12f2..0211f09b6 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -1,119 +1,119 @@ -namespace Ocelot.AcceptanceTests -{ - using IdentityServer4.AccessTokenValidation; - using IdentityServer4.Models; - using IdentityServer4.Test; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Security.Claims; - using TestStack.BDDfy; +namespace Ocelot.AcceptanceTests +{ + using IdentityServer4.AccessTokenValidation; + using IdentityServer4.Models; + using IdentityServer4.Test; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Security.Claims; + using TestStack.BDDfy; using Xunit; - - public class AuthenticationTests : IDisposable - { - private readonly Steps _steps; - private IWebHost _identityServerBuilder; - private string _identityServerRootUrl; - private string _downstreamServicePath = "/"; - private string _downstreamServiceHost = "localhost"; - private string _downstreamServiceScheme = "http"; - private string _downstreamServiceUrl = "http://localhost:"; - private readonly Action _options; - private readonly ServiceHandler _serviceHandler; - - public AuthenticationTests() - { - _serviceHandler = new ServiceHandler(); + + public class AuthenticationTests : IDisposable + { + private readonly Steps _steps; + private IWebHost _identityServerBuilder; + private string _identityServerRootUrl; + private string _downstreamServicePath = "/"; + private string _downstreamServiceHost = "localhost"; + private string _downstreamServiceScheme = "http"; + private string _downstreamServiceUrl = "http://localhost:"; + private readonly Action _options; + private readonly ServiceHandler _serviceHandler; + + public AuthenticationTests() + { + _serviceHandler = new ServiceHandler(); _steps = new Steps(); var identityServerPort = RandomPortFinder.GetRandomPort(); - _identityServerRootUrl = $"http://localhost:{identityServerPort}"; - _options = o => - { - o.Authority = _identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - } - - [Fact] - public void should_return_401_using_identity_server_access_token() - { + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; + _options = o => + { + o.Authority = _identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + } + + [Fact] + public void should_return_401_using_identity_server_access_token() + { int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host =_downstreamServiceHost, - Port = port, - } - }, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - } - } - } - }; - - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 201, string.Empty)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) - .And(x => _steps.GivenThePostHasContent("postContent")) - .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_using_identity_server() - { - int port = RandomPortFinder.GetRandomPort(); + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host =_downstreamServiceHost, + Port = port, + }, + }, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + }, + }, + }, + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 201, string.Empty)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) + .And(x => _steps.GivenThePostHasContent("postContent")) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_using_identity_server() + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host =_downstreamServiceHost, - Port = port, - } - }, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - } - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host =_downstreamServiceHost, + Port = port, + }, + }, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) @@ -125,38 +125,38 @@ public void should_return_response_200_using_identity_server() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_return_response_401_using_identity_server_with_token_requested_for_other_api() - { - int port = RandomPortFinder.GetRandomPort(); - + .BDDfy(); + } + + [Fact] + public void should_return_response_401_using_identity_server_with_token_requested_for_other_api() + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host =_downstreamServiceHost, - Port = port, - } - }, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - } - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host =_downstreamServiceHost, + Port = port, + }, + }, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) @@ -167,38 +167,38 @@ public void should_return_response_401_using_identity_server_with_token_requeste .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) - .BDDfy(); - } - - [Fact] - public void should_return_201_using_identity_server_access_token() - { - int port = RandomPortFinder.GetRandomPort(); - + .BDDfy(); + } + + [Fact] + public void should_return_201_using_identity_server_access_token() + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host =_downstreamServiceHost, - Port = port, - } - }, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - } - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host =_downstreamServiceHost, + Port = port, + }, + }, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) @@ -210,38 +210,38 @@ public void should_return_201_using_identity_server_access_token() .And(x => _steps.GivenThePostHasContent("postContent")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) - .BDDfy(); - } - - [Fact] - public void should_return_201_using_identity_server_reference_token() - { - int port = RandomPortFinder.GetRandomPort(); - + .BDDfy(); + } + + [Fact] + public void should_return_201_using_identity_server_reference_token() + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host =_downstreamServiceHost, - Port = port, - } - }, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - } - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host =_downstreamServiceHost, + Port = port, + }, + }, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test" + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference)) @@ -253,126 +253,126 @@ public void should_return_201_using_identity_server_reference_token() .And(x => _steps.GivenThePostHasContent("postContent")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName, string api2Name, AccessTokenType tokenType) - { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = "My API", - Enabled = true, - DisplayName = "test", - Scopes = new List() - { - new Scope("api"), - new Scope("api.readOnly"), - new Scope("openid"), - new Scope("offline_access") - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId" - } - }, - new ApiResource - { - Name = api2Name, - Description = "My second API", - Enabled = true, - DisplayName = "second test", - Scopes = new List() - { - new Scope("api2"), - new Scope("api2.readOnly"), - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId" - } - }, - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = "client", - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, api2Name, "api.readOnly", "openid", "offline_access" }, - AccessTokenType = tokenType, - Enabled = true, - RequireClientSecret = false - } - }) - .AddTestUsers(new List - { - new TestUser - { - Username = "test", - Password = "test", - SubjectId = "registered|1231231", - Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "321") - } - } - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); - - _identityServerBuilder.Start(); - - _steps.VerifyIdentiryServerStarted(url); - } - - public void Dispose() - { - _serviceHandler.Dispose(); - _steps.Dispose(); - _identityServerBuilder?.Dispose(); - } - } + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, string api2Name, AccessTokenType tokenType) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + new Scope("api"), + new Scope("api.readOnly"), + new Scope("openid"), + new Scope("offline_access"), + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256(), + }, + }, + UserClaims = new List() + { + "CustomerId", "LocationId", + }, + }, + new ApiResource + { + Name = api2Name, + Description = "My second API", + Enabled = true, + DisplayName = "second test", + Scopes = new List() + { + new Scope("api2"), + new Scope("api2.readOnly"), + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256(), + }, + }, + UserClaims = new List() + { + "CustomerId", "LocationId", + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, api2Name, "api.readOnly", "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false, + }, + }) + .AddTestUsers(new List + { + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + Claims = new List + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "321"), + }, + }, + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + _identityServerBuilder?.Dispose(); + } + } } From f16cdd86b028ac6d5f2fa1e83207094a5a044752 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 23 Nov 2020 11:29:50 +0000 Subject: [PATCH 07/34] fix tests and line endings --- .../Ocelot.AcceptanceTests.csproj | 152 +++++++------- ...ekaMiddlewareConfigurationProviderTests.cs | 4 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 192 +++++++++--------- 3 files changed, 174 insertions(+), 174 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 456d002ef..15b13f32a 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -1,77 +1,77 @@ - - - 0.0.0-dev - net5.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 - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - + + + 0.0.0-dev + net5.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 + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs index 15054d822..1837a8927 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs @@ -25,7 +25,7 @@ public void should_not_build() services.AddSingleton(configRepo.Object); var sp = services.BuildServiceProvider(); var provider = EurekaMiddlewareConfigurationProvider.Get(new ApplicationBuilder(sp)); - provider.GetType().BaseType.ShouldBeOfType(); + provider.Status.ShouldBe(TaskStatus.RanToCompletion); } [Fact] @@ -41,7 +41,7 @@ public void should_build() services.AddSingleton(client.Object); var sp = services.BuildServiceProvider(); var provider = EurekaMiddlewareConfigurationProvider.Get(new ApplicationBuilder(sp)); - provider.GetType().BaseType.ShouldBeOfType(); + provider.Status.ShouldBe(TaskStatus.RanToCompletion); } } } diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index ec5cf981c..47a893c7d 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -1,97 +1,97 @@ - - - - 0.0.0-dev - net5.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 - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - - - - all - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + + 0.0.0-dev + net5.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 + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + From 4b23e0ce5faeb29e77e206389168c76a0777b4d7 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 23 Nov 2020 11:35:12 +0000 Subject: [PATCH 08/34] line endings --- build.sh | 234 +++++++++++++++++++++++++++---------------------------- 1 file changed, 117 insertions(+), 117 deletions(-) diff --git a/build.sh b/build.sh index b9e12527f..98a27df3b 100755 --- a/build.sh +++ b/build.sh @@ -1,117 +1,117 @@ -#!/usr/bin/env bash - -########################################################################## -# This is the Cake bootstrapper script for Linux and OS X. -# This file was downloaded from https://github.com/cake-build/resources -# Feel free to change this file to fit your needs. -########################################################################## - -# Define directories. -SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -TOOLS_DIR=$SCRIPT_DIR/tools -ADDINS_DIR=$TOOLS_DIR/Addins -MODULES_DIR=$TOOLS_DIR/Modules -NUGET_EXE=$TOOLS_DIR/nuget.exe -CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe -PACKAGES_CONFIG=$TOOLS_DIR/packages.config -PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum -ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config -MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config - -# Define md5sum or md5 depending on Linux/OSX -MD5_EXE= -if [[ "$(uname -s)" == "Darwin" ]]; then - MD5_EXE="md5 -r" -else - MD5_EXE="md5sum" -fi - -# Define default arguments. -SCRIPT="build.cake" -CAKE_ARGUMENTS=() - -# Parse arguments. -for i in "$@"; do - case $1 in - -s|--script) SCRIPT="$2"; shift ;; - --) shift; CAKE_ARGUMENTS+=("$@"); break ;; - *) CAKE_ARGUMENTS+=("$1") ;; - esac - shift -done - -# Make sure the tools folder exist. -if [ ! -d "$TOOLS_DIR" ]; then - mkdir "$TOOLS_DIR" -fi - -# Make sure that packages.config exist. -if [ ! -f "$TOOLS_DIR/packages.config" ]; then - echo "Downloading packages.config..." - curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages - if [ $? -ne 0 ]; then - echo "An error occurred while downloading packages.config." - exit 1 - fi -fi - -# Download NuGet if it does not exist. -if [ ! -f "$NUGET_EXE" ]; then - echo "Downloading NuGet..." - curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - if [ $? -ne 0 ]; then - echo "An error occurred while downloading nuget.exe." - exit 1 - fi -fi - -# Restore tools from NuGet. -pushd "$TOOLS_DIR" >/dev/null -if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then - find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf -fi - -mono "$NUGET_EXE" install -ExcludeVersion -if [ $? -ne 0 ]; then - echo "Could not restore NuGet tools." - exit 1 -fi - -$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" - -popd >/dev/null - -# Restore addins from NuGet. -if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then - pushd "$ADDINS_DIR" >/dev/null - - mono "$NUGET_EXE" install -ExcludeVersion - if [ $? -ne 0 ]; then - echo "Could not restore NuGet addins." - exit 1 - fi - - popd >/dev/null -fi - -# Restore modules from NuGet. -if [ -f "$MODULES_PACKAGES_CONFIG" ]; then - pushd "$MODULES_DIR" >/dev/null - - mono "$NUGET_EXE" install -ExcludeVersion - if [ $? -ne 0 ]; then - echo "Could not restore NuGet modules." - exit 1 - fi - - popd >/dev/null -fi - -# Make sure that Cake has been installed. -if [ ! -f "$CAKE_EXE" ]; then - echo "Could not find Cake.exe at '$CAKE_EXE'." - exit 1 -fi - -# Start Cake -exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}" +#!/usr/bin/env bash + +########################################################################## +# This is the Cake bootstrapper script for Linux and OS X. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +# Define directories. +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +TOOLS_DIR=$SCRIPT_DIR/tools +ADDINS_DIR=$TOOLS_DIR/Addins +MODULES_DIR=$TOOLS_DIR/Modules +NUGET_EXE=$TOOLS_DIR/nuget.exe +CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe +PACKAGES_CONFIG=$TOOLS_DIR/packages.config +PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum +ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config +MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config + +# Define md5sum or md5 depending on Linux/OSX +MD5_EXE= +if [[ "$(uname -s)" == "Darwin" ]]; then + MD5_EXE="md5 -r" +else + MD5_EXE="md5sum" +fi + +# Define default arguments. +SCRIPT="build.cake" +CAKE_ARGUMENTS=() + +# Parse arguments. +for i in "$@"; do + case $1 in + -s|--script) SCRIPT="$2"; shift ;; + --) shift; CAKE_ARGUMENTS+=("$@"); break ;; + *) CAKE_ARGUMENTS+=("$1") ;; + esac + shift +done + +# Make sure the tools folder exist. +if [ ! -d "$TOOLS_DIR" ]; then + mkdir "$TOOLS_DIR" +fi + +# Make sure that packages.config exist. +if [ ! -f "$TOOLS_DIR/packages.config" ]; then + echo "Downloading packages.config..." + curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages + if [ $? -ne 0 ]; then + echo "An error occurred while downloading packages.config." + exit 1 + fi +fi + +# Download NuGet if it does not exist. +if [ ! -f "$NUGET_EXE" ]; then + echo "Downloading NuGet..." + curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe + if [ $? -ne 0 ]; then + echo "An error occurred while downloading nuget.exe." + exit 1 + fi +fi + +# Restore tools from NuGet. +pushd "$TOOLS_DIR" >/dev/null +if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then + find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf +fi + +mono "$NUGET_EXE" install -ExcludeVersion +if [ $? -ne 0 ]; then + echo "Could not restore NuGet tools." + exit 1 +fi + +$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" + +popd >/dev/null + +# Restore addins from NuGet. +if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then + pushd "$ADDINS_DIR" >/dev/null + + mono "$NUGET_EXE" install -ExcludeVersion + if [ $? -ne 0 ]; then + echo "Could not restore NuGet addins." + exit 1 + fi + + popd >/dev/null +fi + +# Restore modules from NuGet. +if [ -f "$MODULES_PACKAGES_CONFIG" ]; then + pushd "$MODULES_DIR" >/dev/null + + mono "$NUGET_EXE" install -ExcludeVersion + if [ $? -ne 0 ]; then + echo "Could not restore NuGet modules." + exit 1 + fi + + popd >/dev/null +fi + +# Make sure that Cake has been installed. +if [ ! -f "$CAKE_EXE" ]; then + echo "Could not find Cake.exe at '$CAKE_EXE'." + exit 1 +fi + +# Start Cake +exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}" From 0fab351c69a1c047b15daf0cde37c179ca5eed82 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 23 Nov 2020 11:50:19 +0000 Subject: [PATCH 09/34] move to dotnet tools for cake --- .config/dotnet-tools.json | 12 + build.cake | 1092 ++++++++++++++++++------------------- build.ps1 | 258 +-------- build.sh | 119 +--- tools/packages.config | 4 - 5 files changed, 562 insertions(+), 923 deletions(-) create mode 100644 .config/dotnet-tools.json delete mode 100644 tools/packages.config diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 000000000..73c839341 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "0.38.5", + "commands": [ + "dotnet-cake" + ] + } + } +} \ No newline at end of file diff --git a/build.cake b/build.cake index 063de67dc..66f8c43fb 100644 --- a/build.cake +++ b/build.cake @@ -1,547 +1,547 @@ -#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" -#addin nuget:?package=Cake.Json -#addin nuget:?package=Newtonsoft.Json -#addin nuget:?package=System.Net.Http -#addin nuget:?package=System.Text.Encodings.Web -#tool "nuget:?package=ReportGenerator" -#tool "nuget:?package=coveralls.net&version=0.7.0" -#addin Cake.Coveralls&version=0.10.1 - -// compile -var compileConfig = Argument("configuration", "Release"); - -var slnFile = "./Ocelot.sln"; - -// build artifacts -var artifactsDir = Directory("artifacts"); - -// unit testing -var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); -var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; -var minCodeCoverage = 80d; -var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN"; -var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot"; - -// acceptance testing -var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests"); -var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj"; - -// integration testing -var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests"); -var integrationTestAssemblies = @"./test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj"; - -// benchmark testing -var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests"); -var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks"; - -// packaging -var packagesDir = artifactsDir + Directory("Packages"); -var releaseNotesFile = packagesDir + File("releasenotes.md"); -var artifactsFile = packagesDir + File("artifacts.txt"); - -// stable releases -var tagsUrl = "https://api.github.com/repos/ThreeMammals/ocelot/releases/tags/"; -var nugetFeedStableKey = EnvironmentVariable("OCELOT_NUTGET_API_KEY"); -var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package"; -var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; - -// internal build variables - don't change these. -string committedVersion = "0.0.0-dev"; -GitVersion versioning = null; -int releaseId = 0; -string gitHubUsername = "TomPallister"; -string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY"); - -var target = Argument("target", "Default"); - -Information("target is " + target); -Information("Build configuration is " + compileConfig); - -Task("Default") - .IsDependentOn("Build"); - -Task("Build") - .IsDependentOn("RunTests"); - -Task("ReleaseNotes") - .IsDependentOn("CreateReleaseNotes"); - -Task("RunTests") - .IsDependentOn("RunUnitTests") - .IsDependentOn("RunAcceptanceTests") - .IsDependentOn("RunIntegrationTests"); - -Task("Release") - .IsDependentOn("Build") - .IsDependentOn("CreateArtifacts") - .IsDependentOn("PublishGitHubRelease") - .IsDependentOn("PublishToNuget"); - -Task("Compile") - .IsDependentOn("Clean") - .IsDependentOn("Version") - .Does(() => - { - var settings = new DotNetCoreBuildSettings - { - Configuration = compileConfig, - }; - - DotNetCoreBuild(slnFile, settings); - }); - -Task("Clean") - .Does(() => - { - if (DirectoryExists(artifactsDir)) - { - DeleteDirectory(artifactsDir, recursive:true); - } - CreateDirectory(artifactsDir); - }); - -Task("CreateReleaseNotes") - .Does(() => - { - Information("Generating release notes at " + releaseNotesFile); - - IEnumerable lastReleaseTag; - - var lastReleaseTagExitCode = StartProcess( - "git", - new ProcessSettings { - Arguments = "describe --tags --abbrev=0", - RedirectStandardOutput = true - }, - out lastReleaseTag - ); - - if (lastReleaseTagExitCode != 0) - { - throw new Exception("Failed to get latest release tag"); - } - - var lastRelease = lastReleaseTag.First(); - - Information("Last release tag is " + lastRelease); - - IEnumerable releaseNotes; - - var releaseNotesExitCode = StartProcess( - "git", - new ProcessSettings { - Arguments = $"log --pretty=format:\"%h - %an - %s\" {lastRelease}..HEAD", - RedirectStandardOutput = true - }, - out releaseNotes - ); - - if (releaseNotesExitCode != 0) - { - throw new Exception("Failed to generate release notes"); - } - - EnsureDirectoryExists(packagesDir); - - System.IO.File.WriteAllLines(releaseNotesFile, releaseNotes); - - if (string.IsNullOrEmpty(System.IO.File.ReadAllText(releaseNotesFile))) - { - System.IO.File.WriteAllText(releaseNotesFile, "No commits since last release"); - } - - Information("Release notes are\r\n" + System.IO.File.ReadAllText(releaseNotesFile)); - }); - -Task("Version") - .IsDependentOn("CreateReleaseNotes") - .Does(() => - { - versioning = GetNuGetVersionForCommit(); - var nugetVersion = versioning.NuGetVersion; - Information("SemVer version number: " + nugetVersion); - - if (IsRunningOnCircleCI()) - { - Information("Persisting version number..."); - PersistVersion(committedVersion, nugetVersion); - } - else - { - Information("We are not running on build server, so we won't persist the version number."); - } - }); - -Task("RunUnitTests") - .IsDependentOn("Compile") - .Does(() => - { - var testSettings = new DotNetCoreTestSettings - { - Configuration = compileConfig, - ResultsDirectory = artifactsForUnitTestsDir, - ArgumentCustomization = args => args - // this create the code coverage report - .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") - }; - - EnsureDirectoryExists(artifactsForUnitTestsDir); - DotNetCoreTest(unitTestAssemblies, testSettings); - - var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); - Information(coverageSummaryFile); - Information(artifactsForUnitTestsDir); - - // todo bring back report generator to get a friendly report - // ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); - // https://github.com/danielpalme/ReportGenerator - - if (IsRunningOnCircleCI() && IsMaster()) - { - var repoToken = EnvironmentVariable(coverallsRepoToken); - if (string.IsNullOrEmpty(repoToken)) - { - throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken)); - } - - Information(string.Format("Uploading test coverage to {0}", coverallsRepo)); - CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings() - { - RepoToken = repoToken - }); - } - else - { - Information("We are not running on the build server so we won't publish the coverage report to coveralls.io"); - } - - var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage"); - var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage"); - - Information("Sequence Coverage: " + sequenceCoverage); - - if(double.Parse(sequenceCoverage) < minCodeCoverage) - { - var whereToCheck = !IsRunningOnCircleCI() ? coverallsRepo : artifactsForUnitTestsDir; - throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck)); - }; - }); - -Task("RunAcceptanceTests") - .IsDependentOn("Compile") - .Does(() => - { - var settings = new DotNetCoreTestSettings - { - Configuration = compileConfig, - ArgumentCustomization = args => args - .Append("--no-restore") - .Append("--no-build") - }; - - EnsureDirectoryExists(artifactsForAcceptanceTestsDir); - DotNetCoreTest(acceptanceTestAssemblies, settings); - }); - -Task("RunIntegrationTests") - .IsDependentOn("Compile") - .Does(() => - { - var settings = new DotNetCoreTestSettings - { - Configuration = compileConfig, - ArgumentCustomization = args => args - .Append("--no-restore") - .Append("--no-build") - }; - - EnsureDirectoryExists(artifactsForIntegrationTestsDir); - DotNetCoreTest(integrationTestAssemblies, settings); - }); - -Task("CreateArtifacts") - .IsDependentOn("Compile") - .Does(() => - { - EnsureDirectoryExists(packagesDir); - - CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir); - - var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg"); - - foreach(var projectFile in projectFiles) - { - System.IO.File.AppendAllLines(artifactsFile, new[]{ - projectFile.GetFilename().FullPath, - "releasenotes.md" - }); - } - - var artifacts = System.IO.File - .ReadAllLines(artifactsFile) - .Distinct(); - - foreach(var artifact in artifacts) - { - var codePackage = packagesDir + File(artifact); - - Information("Created package " + codePackage); - } - }); - -Task("PublishGitHubRelease") - .IsDependentOn("CreateArtifacts") - .Does(() => - { - if (IsRunningOnCircleCI()) - { - var path = packagesDir.ToString() + @"/**/*"; - - CreateGitHubRelease(); - - foreach (var file in GetFiles(path)) - { - UploadFileToGitHubRelease(file); - } - - CompleteGitHubRelease(); - } - }); - -Task("EnsureStableReleaseRequirements") - .Does(() => - { - Information("Check if stable release..."); - - if (!IsRunningOnCircleCI()) - { - throw new Exception("Stable release should happen via circleci"); - } - - Information("Release is stable..."); - }); - -Task("DownloadGitHubReleaseArtifacts") - .Does(() => - { - - try - { - // hack to let GitHub catch up, todo - refactor to poll - System.Threading.Thread.Sleep(5000); - - EnsureDirectoryExists(packagesDir); - - var releaseUrl = tagsUrl + versioning.NuGetVersion; - - var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) - .Value("assets_url"); - - var assets = GetResource(assets_url); - - foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) - { - var file = packagesDir + File(asset.Value("name")); - DownloadFile(asset.Value("browser_download_url"), file); - } - } - catch(Exception exception) - { - Information("There was an exception " + exception); - throw; - } - }); - -Task("PublishToNuget") - .IsDependentOn("DownloadGitHubReleaseArtifacts") - .Does(() => - { - if (IsRunningOnCircleCI()) - { - PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); - } - }); - -RunTarget(target); - -/// Gets unique nuget version for this commit -private GitVersion GetNuGetVersionForCommit() -{ - GitVersion(new GitVersionSettings{ - UpdateAssemblyInfo = false, - OutputType = GitVersionOutput.BuildServer - }); - - return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json }); -} - -/// Updates project version in all of our projects -private void PersistVersion(string committedVersion, string newVersion) -{ - Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion)); - - var projectFiles = GetFiles("./**/*.csproj"); - - foreach(var projectFile in projectFiles) - { - var file = projectFile.ToString(); - - Information(string.Format("Updating {0}...", file)); - - var updatedProjectFile = System.IO.File.ReadAllText(file) - .Replace(committedVersion, newVersion); - - System.IO.File.WriteAllText(file, updatedProjectFile); - } -} - -/// Publishes code and symbols packages to nuget feed, based on contents of artifacts file -private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl) -{ - Information("PublishPackages"); - var artifacts = System.IO.File - .ReadAllLines(artifactsFile) - .Distinct(); - - foreach(var artifact in artifacts) - { - if (artifact == "releasenotes.md") - { - continue; - } - - var codePackage = packagesDir + File(artifact); - - Information("Pushing package " + codePackage); - - Information("Calling NuGetPush"); - - NuGetPush( - codePackage, - new NuGetPushSettings { - ApiKey = feedApiKey, - Source = codeFeedUrl - }); - } -} - -private void CreateGitHubRelease() -{ - var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"{ReleaseNotesAsJson()}\", \"draft\": true, \"prerelease\": true }}"; - - var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); - - using(var client = new System.Net.Http.HttpClient()) - { - client.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue( - "Basic", Convert.ToBase64String( - System.Text.ASCIIEncoding.ASCII.GetBytes( - $"{gitHubUsername}:{gitHubPassword}"))); - - client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); - - var result = client.PostAsync("https://api.github.com/repos/ThreeMammals/Ocelot/releases", content).Result; - if(result.StatusCode != System.Net.HttpStatusCode.Created) - { - throw new Exception("CreateGitHubRelease result.StatusCode = " + result.StatusCode); - } - var returnValue = result.Content.ReadAsStringAsync().Result; - dynamic test = Newtonsoft.Json.JsonConvert.DeserializeObject(returnValue); - releaseId = test.id; - } -} - -private string ReleaseNotesAsJson() -{ - return System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode(System.IO.File.ReadAllText(releaseNotesFile)); -} - -private void UploadFileToGitHubRelease(FilePath file) -{ - var data = System.IO.File.ReadAllBytes(file.FullPath); - var content = new System.Net.Http.ByteArrayContent(data); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); - - using(var client = new System.Net.Http.HttpClient()) - { - client.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue( - "Basic", Convert.ToBase64String( - System.Text.ASCIIEncoding.ASCII.GetBytes( - $"{gitHubUsername}:{gitHubPassword}"))); - - client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); - - var result = client.PostAsync($"https://uploads.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}/assets?name={file.GetFilename()}", content).Result; - if(result.StatusCode != System.Net.HttpStatusCode.Created) - { - throw new Exception("UploadFileToGitHubRelease result.StatusCode = " + result.StatusCode); - } - } -} - -private void CompleteGitHubRelease() -{ - var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"{ReleaseNotesAsJson()}\", \"draft\": false, \"prerelease\": false }}"; - var request = new System.Net.Http.HttpRequestMessage(new System.Net.Http.HttpMethod("Patch"), $"https://api.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}"); - request.Content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); - - using(var client = new System.Net.Http.HttpClient()) - { - client.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue( - "Basic", Convert.ToBase64String( - System.Text.ASCIIEncoding.ASCII.GetBytes( - $"{gitHubUsername}:{gitHubPassword}"))); - - client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); - - var result = client.SendAsync(request).Result; - if(result.StatusCode != System.Net.HttpStatusCode.OK) - { - throw new Exception("CompleteGitHubRelease result.StatusCode = " + result.StatusCode); - } - } -} - - -/// gets the resource from the specified url -private string GetResource(string url) -{ - try - { - Information("Getting resource from " + url); - - var assetsRequest = System.Net.WebRequest.CreateHttp(url); - assetsRequest.Method = "GET"; - assetsRequest.Accept = "application/vnd.github.v3+json"; - assetsRequest.UserAgent = "BuildScript"; - - using (var assetsResponse = assetsRequest.GetResponse()) - { - var assetsStream = assetsResponse.GetResponseStream(); - var assetsReader = new StreamReader(assetsStream); - var response = assetsReader.ReadToEnd(); - - Information("Response is " + response); - - return response; - } - } - catch(Exception exception) - { - Information("There was an exception " + exception); - throw; - } -} - -private bool IsRunningOnCircleCI() -{ - return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI")); -} - -private bool IsMaster() -{ - return Environment.GetEnvironmentVariable("CIRCLE_BRANCH").ToLower() == "master"; +#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" +#addin nuget:?package=Cake.Json +#addin nuget:?package=Newtonsoft.Json +#addin nuget:?package=System.Net.Http +#addin nuget:?package=System.Text.Encodings.Web&version=4.7.1 +#tool "nuget:?package=ReportGenerator" +#tool "nuget:?package=coveralls.net&version=0.7.0" +#addin Cake.Coveralls&version=0.10.1 + +// compile +var compileConfig = Argument("configuration", "Release"); + +var slnFile = "./Ocelot.sln"; + +// build artifacts +var artifactsDir = Directory("artifacts"); + +// unit testing +var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); +var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; +var minCodeCoverage = 80d; +var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN"; +var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot"; + +// acceptance testing +var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests"); +var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj"; + +// integration testing +var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests"); +var integrationTestAssemblies = @"./test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj"; + +// benchmark testing +var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests"); +var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks"; + +// packaging +var packagesDir = artifactsDir + Directory("Packages"); +var releaseNotesFile = packagesDir + File("releasenotes.md"); +var artifactsFile = packagesDir + File("artifacts.txt"); + +// stable releases +var tagsUrl = "https://api.github.com/repos/ThreeMammals/ocelot/releases/tags/"; +var nugetFeedStableKey = EnvironmentVariable("OCELOT_NUTGET_API_KEY"); +var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package"; +var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; + +// internal build variables - don't change these. +string committedVersion = "0.0.0-dev"; +GitVersion versioning = null; +int releaseId = 0; +string gitHubUsername = "TomPallister"; +string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY"); + +var target = Argument("target", "Default"); + +Information("target is " + target); +Information("Build configuration is " + compileConfig); + +Task("Default") + .IsDependentOn("Build"); + +Task("Build") + .IsDependentOn("RunTests"); + +Task("ReleaseNotes") + .IsDependentOn("CreateReleaseNotes"); + +Task("RunTests") + .IsDependentOn("RunUnitTests") + .IsDependentOn("RunAcceptanceTests") + .IsDependentOn("RunIntegrationTests"); + +Task("Release") + .IsDependentOn("Build") + .IsDependentOn("CreateArtifacts") + .IsDependentOn("PublishGitHubRelease") + .IsDependentOn("PublishToNuget"); + +Task("Compile") + .IsDependentOn("Clean") + .IsDependentOn("Version") + .Does(() => + { + var settings = new DotNetCoreBuildSettings + { + Configuration = compileConfig, + }; + + DotNetCoreBuild(slnFile, settings); + }); + +Task("Clean") + .Does(() => + { + if (DirectoryExists(artifactsDir)) + { + DeleteDirectory(artifactsDir, recursive:true); + } + CreateDirectory(artifactsDir); + }); + +Task("CreateReleaseNotes") + .Does(() => + { + Information("Generating release notes at " + releaseNotesFile); + + IEnumerable lastReleaseTag; + + var lastReleaseTagExitCode = StartProcess( + "git", + new ProcessSettings { + Arguments = "describe --tags --abbrev=0", + RedirectStandardOutput = true + }, + out lastReleaseTag + ); + + if (lastReleaseTagExitCode != 0) + { + throw new Exception("Failed to get latest release tag"); + } + + var lastRelease = lastReleaseTag.First(); + + Information("Last release tag is " + lastRelease); + + IEnumerable releaseNotes; + + var releaseNotesExitCode = StartProcess( + "git", + new ProcessSettings { + Arguments = $"log --pretty=format:\"%h - %an - %s\" {lastRelease}..HEAD", + RedirectStandardOutput = true + }, + out releaseNotes + ); + + if (releaseNotesExitCode != 0) + { + throw new Exception("Failed to generate release notes"); + } + + EnsureDirectoryExists(packagesDir); + + System.IO.File.WriteAllLines(releaseNotesFile, releaseNotes); + + if (string.IsNullOrEmpty(System.IO.File.ReadAllText(releaseNotesFile))) + { + System.IO.File.WriteAllText(releaseNotesFile, "No commits since last release"); + } + + Information("Release notes are\r\n" + System.IO.File.ReadAllText(releaseNotesFile)); + }); + +Task("Version") + .IsDependentOn("CreateReleaseNotes") + .Does(() => + { + versioning = GetNuGetVersionForCommit(); + var nugetVersion = versioning.NuGetVersion; + Information("SemVer version number: " + nugetVersion); + + if (IsRunningOnCircleCI()) + { + Information("Persisting version number..."); + PersistVersion(committedVersion, nugetVersion); + } + else + { + Information("We are not running on build server, so we won't persist the version number."); + } + }); + +Task("RunUnitTests") + .IsDependentOn("Compile") + .Does(() => + { + var testSettings = new DotNetCoreTestSettings + { + Configuration = compileConfig, + ResultsDirectory = artifactsForUnitTestsDir, + ArgumentCustomization = args => args + // this create the code coverage report + .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") + }; + + EnsureDirectoryExists(artifactsForUnitTestsDir); + DotNetCoreTest(unitTestAssemblies, testSettings); + + var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); + Information(coverageSummaryFile); + Information(artifactsForUnitTestsDir); + + // todo bring back report generator to get a friendly report + // ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); + // https://github.com/danielpalme/ReportGenerator + + if (IsRunningOnCircleCI() && IsMaster()) + { + var repoToken = EnvironmentVariable(coverallsRepoToken); + if (string.IsNullOrEmpty(repoToken)) + { + throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken)); + } + + Information(string.Format("Uploading test coverage to {0}", coverallsRepo)); + CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings() + { + RepoToken = repoToken + }); + } + else + { + Information("We are not running on the build server so we won't publish the coverage report to coveralls.io"); + } + + var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage"); + var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage"); + + Information("Sequence Coverage: " + sequenceCoverage); + + if(double.Parse(sequenceCoverage) < minCodeCoverage) + { + var whereToCheck = !IsRunningOnCircleCI() ? coverallsRepo : artifactsForUnitTestsDir; + throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck)); + }; + }); + +Task("RunAcceptanceTests") + .IsDependentOn("Compile") + .Does(() => + { + var settings = new DotNetCoreTestSettings + { + Configuration = compileConfig, + ArgumentCustomization = args => args + .Append("--no-restore") + .Append("--no-build") + }; + + EnsureDirectoryExists(artifactsForAcceptanceTestsDir); + DotNetCoreTest(acceptanceTestAssemblies, settings); + }); + +Task("RunIntegrationTests") + .IsDependentOn("Compile") + .Does(() => + { + var settings = new DotNetCoreTestSettings + { + Configuration = compileConfig, + ArgumentCustomization = args => args + .Append("--no-restore") + .Append("--no-build") + }; + + EnsureDirectoryExists(artifactsForIntegrationTestsDir); + DotNetCoreTest(integrationTestAssemblies, settings); + }); + +Task("CreateArtifacts") + .IsDependentOn("Compile") + .Does(() => + { + EnsureDirectoryExists(packagesDir); + + CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir); + + var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg"); + + foreach(var projectFile in projectFiles) + { + System.IO.File.AppendAllLines(artifactsFile, new[]{ + projectFile.GetFilename().FullPath, + "releasenotes.md" + }); + } + + var artifacts = System.IO.File + .ReadAllLines(artifactsFile) + .Distinct(); + + foreach(var artifact in artifacts) + { + var codePackage = packagesDir + File(artifact); + + Information("Created package " + codePackage); + } + }); + +Task("PublishGitHubRelease") + .IsDependentOn("CreateArtifacts") + .Does(() => + { + if (IsRunningOnCircleCI()) + { + var path = packagesDir.ToString() + @"/**/*"; + + CreateGitHubRelease(); + + foreach (var file in GetFiles(path)) + { + UploadFileToGitHubRelease(file); + } + + CompleteGitHubRelease(); + } + }); + +Task("EnsureStableReleaseRequirements") + .Does(() => + { + Information("Check if stable release..."); + + if (!IsRunningOnCircleCI()) + { + throw new Exception("Stable release should happen via circleci"); + } + + Information("Release is stable..."); + }); + +Task("DownloadGitHubReleaseArtifacts") + .Does(() => + { + + try + { + // hack to let GitHub catch up, todo - refactor to poll + System.Threading.Thread.Sleep(5000); + + EnsureDirectoryExists(packagesDir); + + var releaseUrl = tagsUrl + versioning.NuGetVersion; + + var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) + .Value("assets_url"); + + var assets = GetResource(assets_url); + + foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) + { + var file = packagesDir + File(asset.Value("name")); + DownloadFile(asset.Value("browser_download_url"), file); + } + } + catch(Exception exception) + { + Information("There was an exception " + exception); + throw; + } + }); + +Task("PublishToNuget") + .IsDependentOn("DownloadGitHubReleaseArtifacts") + .Does(() => + { + if (IsRunningOnCircleCI()) + { + PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); + } + }); + +RunTarget(target); + +/// Gets unique nuget version for this commit +private GitVersion GetNuGetVersionForCommit() +{ + GitVersion(new GitVersionSettings{ + UpdateAssemblyInfo = false, + OutputType = GitVersionOutput.BuildServer + }); + + return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json }); +} + +/// Updates project version in all of our projects +private void PersistVersion(string committedVersion, string newVersion) +{ + Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion)); + + var projectFiles = GetFiles("./**/*.csproj"); + + foreach(var projectFile in projectFiles) + { + var file = projectFile.ToString(); + + Information(string.Format("Updating {0}...", file)); + + var updatedProjectFile = System.IO.File.ReadAllText(file) + .Replace(committedVersion, newVersion); + + System.IO.File.WriteAllText(file, updatedProjectFile); + } +} + +/// Publishes code and symbols packages to nuget feed, based on contents of artifacts file +private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl) +{ + Information("PublishPackages"); + var artifacts = System.IO.File + .ReadAllLines(artifactsFile) + .Distinct(); + + foreach(var artifact in artifacts) + { + if (artifact == "releasenotes.md") + { + continue; + } + + var codePackage = packagesDir + File(artifact); + + Information("Pushing package " + codePackage); + + Information("Calling NuGetPush"); + + NuGetPush( + codePackage, + new NuGetPushSettings { + ApiKey = feedApiKey, + Source = codeFeedUrl + }); + } +} + +private void CreateGitHubRelease() +{ + var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"{ReleaseNotesAsJson()}\", \"draft\": true, \"prerelease\": true }}"; + + var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); + + using(var client = new System.Net.Http.HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue( + "Basic", Convert.ToBase64String( + System.Text.ASCIIEncoding.ASCII.GetBytes( + $"{gitHubUsername}:{gitHubPassword}"))); + + client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); + + var result = client.PostAsync("https://api.github.com/repos/ThreeMammals/Ocelot/releases", content).Result; + if(result.StatusCode != System.Net.HttpStatusCode.Created) + { + throw new Exception("CreateGitHubRelease result.StatusCode = " + result.StatusCode); + } + var returnValue = result.Content.ReadAsStringAsync().Result; + dynamic test = Newtonsoft.Json.JsonConvert.DeserializeObject(returnValue); + releaseId = test.id; + } +} + +private string ReleaseNotesAsJson() +{ + return System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode(System.IO.File.ReadAllText(releaseNotesFile)); +} + +private void UploadFileToGitHubRelease(FilePath file) +{ + var data = System.IO.File.ReadAllBytes(file.FullPath); + var content = new System.Net.Http.ByteArrayContent(data); + content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); + + using(var client = new System.Net.Http.HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue( + "Basic", Convert.ToBase64String( + System.Text.ASCIIEncoding.ASCII.GetBytes( + $"{gitHubUsername}:{gitHubPassword}"))); + + client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); + + var result = client.PostAsync($"https://uploads.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}/assets?name={file.GetFilename()}", content).Result; + if(result.StatusCode != System.Net.HttpStatusCode.Created) + { + throw new Exception("UploadFileToGitHubRelease result.StatusCode = " + result.StatusCode); + } + } +} + +private void CompleteGitHubRelease() +{ + var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"{ReleaseNotesAsJson()}\", \"draft\": false, \"prerelease\": false }}"; + var request = new System.Net.Http.HttpRequestMessage(new System.Net.Http.HttpMethod("Patch"), $"https://api.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}"); + request.Content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); + + using(var client = new System.Net.Http.HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue( + "Basic", Convert.ToBase64String( + System.Text.ASCIIEncoding.ASCII.GetBytes( + $"{gitHubUsername}:{gitHubPassword}"))); + + client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); + + var result = client.SendAsync(request).Result; + if(result.StatusCode != System.Net.HttpStatusCode.OK) + { + throw new Exception("CompleteGitHubRelease result.StatusCode = " + result.StatusCode); + } + } +} + + +/// gets the resource from the specified url +private string GetResource(string url) +{ + try + { + Information("Getting resource from " + url); + + var assetsRequest = System.Net.WebRequest.CreateHttp(url); + assetsRequest.Method = "GET"; + assetsRequest.Accept = "application/vnd.github.v3+json"; + assetsRequest.UserAgent = "BuildScript"; + + using (var assetsResponse = assetsRequest.GetResponse()) + { + var assetsStream = assetsResponse.GetResponseStream(); + var assetsReader = new StreamReader(assetsStream); + var response = assetsReader.ReadToEnd(); + + Information("Response is " + response); + + return response; + } + } + catch(Exception exception) + { + Information("There was an exception " + exception); + throw; + } +} + +private bool IsRunningOnCircleCI() +{ + return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI")); +} + +private bool IsMaster() +{ + return Environment.GetEnvironmentVariable("CIRCLE_BRANCH").ToLower() == "master"; } \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index a336e2985..80bf4e64c 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,256 +1,2 @@ -########################################################################## -# This is the Cake bootstrapper script for PowerShell. -# This file was downloaded from https://github.com/cake-build/resources -# Feel free to change this file to fit your needs. -########################################################################## - -<# - -.SYNOPSIS -This is a Powershell script to bootstrap a Cake build. - -.DESCRIPTION -This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) -and execute your Cake build script with the parameters you provide. - -.PARAMETER Script -The build script to execute. -.PARAMETER Target -The build script target to run. -.PARAMETER Configuration -The build configuration to use. -.PARAMETER Verbosity -Specifies the amount of information to be displayed. -.PARAMETER ShowDescription -Shows description about tasks. -.PARAMETER DryRun -Performs a dry run. -.PARAMETER SkipToolPackageRestore -Skips restoring of packages. -.PARAMETER ScriptArgs -Remaining arguments are added here. - -.LINK -https://cakebuild.net - -#> - -[CmdletBinding()] -Param( - [string]$Script = "build.cake", - [string]$Target, - [string]$Configuration, - [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] - [string]$Verbosity, - [switch]$ShowDescription, - [Alias("WhatIf", "Noop")] - [switch]$DryRun, - [switch]$SkipToolPackageRestore, - [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] - [string[]]$ScriptArgs -) - -# Attempt to set highest encryption available for SecurityProtocol. -# PowerShell will not set this by default (until maybe .NET 4.6.x). This -# will typically produce a message for PowerShell v2 (just an info -# message though) -try { - # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) - # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't - # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is - # installed (.NET 4.5 is an in-place upgrade). - # PowerShell Core already has support for TLS 1.2 so we can skip this if running in that. - if (-not $IsCoreCLR) { - [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 - } - } catch { - Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' - } - -[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null -function MD5HashFile([string] $filePath) -{ - if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) - { - return $null - } - - [System.IO.Stream] $file = $null; - [System.Security.Cryptography.MD5] $md5 = $null; - try - { - $md5 = [System.Security.Cryptography.MD5]::Create() - $file = [System.IO.File]::OpenRead($filePath) - return [System.BitConverter]::ToString($md5.ComputeHash($file)) - } - finally - { - if ($file -ne $null) - { - $file.Dispose() - } - } -} - -function GetProxyEnabledWebClient -{ - $wc = New-Object System.Net.WebClient - $proxy = [System.Net.WebRequest]::GetSystemWebProxy() - $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials - $wc.Proxy = $proxy - return $wc -} - -Write-Host "Preparing to run build script..." - -if(!$PSScriptRoot){ - $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent -} - -$TOOLS_DIR = Join-Path $PSScriptRoot "tools" -$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" -$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" -$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" -$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" -$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" -$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" -$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" -$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" - -# Make sure tools folder exists -if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { - Write-Verbose -Message "Creating tools directory..." - New-Item -Path $TOOLS_DIR -Type Directory | Out-Null -} - -# Make sure that packages.config exist. -if (!(Test-Path $PACKAGES_CONFIG)) { - Write-Verbose -Message "Downloading packages.config..." - try { - $wc = GetProxyEnabledWebClient - $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) - } catch { - Throw "Could not download packages.config." - } -} - -# Try find NuGet.exe in path if not exists -if (!(Test-Path $NUGET_EXE)) { - Write-Verbose -Message "Trying to find nuget.exe in PATH..." - $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } - $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 - if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { - Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." - $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName - } -} - -# Try download NuGet.exe if not exists -if (!(Test-Path $NUGET_EXE)) { - Write-Verbose -Message "Downloading NuGet.exe..." - try { - $wc = GetProxyEnabledWebClient - $wc.DownloadFile($NUGET_URL, $NUGET_EXE) - } catch { - Throw "Could not download NuGet.exe." - } -} - -# Save nuget.exe path to environment to be available to child processed -$env:NUGET_EXE = $NUGET_EXE -$env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { - "mono `"$NUGET_EXE`"" -} else { - "`"$NUGET_EXE`"" -} - -# Restore tools from NuGet? -if(-Not $SkipToolPackageRestore.IsPresent) { - Push-Location - Set-Location $TOOLS_DIR - - # Check for changes in packages.config and remove installed tools if true. - [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG - if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or - ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { - Write-Verbose -Message "Missing or changed package.config hash..." - Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | - Remove-Item -Recurse -Force - } - - Write-Verbose -Message "Restoring tools from NuGet..." - - $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet tools." - } - else - { - $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" - } - Write-Verbose -Message ($NuGetOutput | Out-String) - - Pop-Location -} - -# Restore addins from NuGet -if (Test-Path $ADDINS_PACKAGES_CONFIG) { - Push-Location - Set-Location $ADDINS_DIR - - Write-Verbose -Message "Restoring addins from NuGet..." - $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet addins." - } - - Write-Verbose -Message ($NuGetOutput | Out-String) - - Pop-Location -} - -# Restore modules from NuGet -if (Test-Path $MODULES_PACKAGES_CONFIG) { - Push-Location - Set-Location $MODULES_DIR - - Write-Verbose -Message "Restoring modules from NuGet..." - $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet modules." - } - - Write-Verbose -Message ($NuGetOutput | Out-String) - - Pop-Location -} - -# Make sure that Cake has been installed. -if (!(Test-Path $CAKE_EXE)) { - Throw "Could not find Cake.exe at $CAKE_EXE" -} - -$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { - "mono `"$CAKE_EXE`"" -} else { - "`"$CAKE_EXE`"" -} - - # Build an array (not a string) of Cake arguments to be joined later -$cakeArguments = @() -if ($Script) { $cakeArguments += "`"$Script`"" } -if ($Target) { $cakeArguments += "-target=`"$Target`"" } -if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } -if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } -if ($ShowDescription) { $cakeArguments += "-showdescription" } -if ($DryRun) { $cakeArguments += "-dryrun" } -$cakeArguments += $ScriptArgs - -# Start Cake -Write-Host "Running build script..." -Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")" -exit $LASTEXITCODE +dotnet tool install Cake.Tool --version 0.38.5 +dotnet cake diff --git a/build.sh b/build.sh index 98a27df3b..80bf4e64c 100755 --- a/build.sh +++ b/build.sh @@ -1,117 +1,2 @@ -#!/usr/bin/env bash - -########################################################################## -# This is the Cake bootstrapper script for Linux and OS X. -# This file was downloaded from https://github.com/cake-build/resources -# Feel free to change this file to fit your needs. -########################################################################## - -# Define directories. -SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -TOOLS_DIR=$SCRIPT_DIR/tools -ADDINS_DIR=$TOOLS_DIR/Addins -MODULES_DIR=$TOOLS_DIR/Modules -NUGET_EXE=$TOOLS_DIR/nuget.exe -CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe -PACKAGES_CONFIG=$TOOLS_DIR/packages.config -PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum -ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config -MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config - -# Define md5sum or md5 depending on Linux/OSX -MD5_EXE= -if [[ "$(uname -s)" == "Darwin" ]]; then - MD5_EXE="md5 -r" -else - MD5_EXE="md5sum" -fi - -# Define default arguments. -SCRIPT="build.cake" -CAKE_ARGUMENTS=() - -# Parse arguments. -for i in "$@"; do - case $1 in - -s|--script) SCRIPT="$2"; shift ;; - --) shift; CAKE_ARGUMENTS+=("$@"); break ;; - *) CAKE_ARGUMENTS+=("$1") ;; - esac - shift -done - -# Make sure the tools folder exist. -if [ ! -d "$TOOLS_DIR" ]; then - mkdir "$TOOLS_DIR" -fi - -# Make sure that packages.config exist. -if [ ! -f "$TOOLS_DIR/packages.config" ]; then - echo "Downloading packages.config..." - curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages - if [ $? -ne 0 ]; then - echo "An error occurred while downloading packages.config." - exit 1 - fi -fi - -# Download NuGet if it does not exist. -if [ ! -f "$NUGET_EXE" ]; then - echo "Downloading NuGet..." - curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - if [ $? -ne 0 ]; then - echo "An error occurred while downloading nuget.exe." - exit 1 - fi -fi - -# Restore tools from NuGet. -pushd "$TOOLS_DIR" >/dev/null -if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then - find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf -fi - -mono "$NUGET_EXE" install -ExcludeVersion -if [ $? -ne 0 ]; then - echo "Could not restore NuGet tools." - exit 1 -fi - -$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" - -popd >/dev/null - -# Restore addins from NuGet. -if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then - pushd "$ADDINS_DIR" >/dev/null - - mono "$NUGET_EXE" install -ExcludeVersion - if [ $? -ne 0 ]; then - echo "Could not restore NuGet addins." - exit 1 - fi - - popd >/dev/null -fi - -# Restore modules from NuGet. -if [ -f "$MODULES_PACKAGES_CONFIG" ]; then - pushd "$MODULES_DIR" >/dev/null - - mono "$NUGET_EXE" install -ExcludeVersion - if [ $? -ne 0 ]; then - echo "Could not restore NuGet modules." - exit 1 - fi - - popd >/dev/null -fi - -# Make sure that Cake has been installed. -if [ ! -f "$CAKE_EXE" ]; then - echo "Could not find Cake.exe at '$CAKE_EXE'." - exit 1 -fi - -# Start Cake -exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}" +dotnet tool install Cake.Tool --version 0.38.5 +dotnet cake diff --git a/tools/packages.config b/tools/packages.config deleted file mode 100644 index 238f21ca0..000000000 --- a/tools/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - From 1185576ba98401412b5c1549b6fdcd4fcca57692 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 23 Nov 2020 12:06:54 +0000 Subject: [PATCH 10/34] wip --- Ocelot.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/Ocelot.sln b/Ocelot.sln index 511c79839..922f62261 100644 --- a/Ocelot.sln +++ b/Ocelot.sln @@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore build.cake = build.cake build.ps1 = build.ps1 + build.sh = build.sh codeanalysis.ruleset = codeanalysis.ruleset .circleci\config.yml = .circleci\config.yml GitVersion.yml = GitVersion.yml From a5a1606c3e0348b44f373643752565fc3371b089 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 23 Nov 2020 12:13:27 +0000 Subject: [PATCH 11/34] upgrade dotnet test and coverages packages --- build.cake | 4 +- .../Ocelot.AcceptanceTests.csproj | 2 +- .../Ocelot.IntegrationTests.csproj | 2 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 2 +- test/Ocelot.UnitTests/UnitTests.runsettings | 44 +++++++++---------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/build.cake b/build.cake index 66f8c43fb..50dfd5c49 100644 --- a/build.cake +++ b/build.cake @@ -4,8 +4,8 @@ #addin nuget:?package=System.Net.Http #addin nuget:?package=System.Text.Encodings.Web&version=4.7.1 #tool "nuget:?package=ReportGenerator" -#tool "nuget:?package=coveralls.net&version=0.7.0" -#addin Cake.Coveralls&version=0.10.1 +#tool "nuget:?package=coveralls.net&version=2.0.0" +#addin Cake.Coveralls&version=0.10.2 // compile var compileConfig = Argument("configuration", "Release"); diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 15b13f32a..2552ec47f 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -40,7 +40,7 @@ - + diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index fb156952d..0fc66ff36 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -31,7 +31,7 @@ - + all diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 47a893c7d..35aa633d4 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -51,7 +51,7 @@ - + all diff --git a/test/Ocelot.UnitTests/UnitTests.runsettings b/test/Ocelot.UnitTests/UnitTests.runsettings index 1cba21cc9..80c9b34f5 100644 --- a/test/Ocelot.UnitTests/UnitTests.runsettings +++ b/test/Ocelot.UnitTests/UnitTests.runsettings @@ -1,23 +1,23 @@ - - - - - - - opencover - false - true - - - - - - - - - - + + + + + + + opencover + false + true + + + + + + + + + + \ No newline at end of file From 71a1053d704a147279818a801b475e9d53d5ea95 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 23 Nov 2020 12:28:11 +0000 Subject: [PATCH 12/34] build works on windows --- build.cake | 14 +++++++------- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.cake b/build.cake index 50dfd5c49..6dda8a402 100644 --- a/build.cake +++ b/build.cake @@ -18,7 +18,7 @@ var artifactsDir = Directory("artifacts"); // unit testing var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; -var minCodeCoverage = 80d; +var minCodeCoverage = 0.80d; var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN"; var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot"; @@ -180,15 +180,15 @@ Task("RunUnitTests") { Configuration = compileConfig, ResultsDirectory = artifactsForUnitTestsDir, - ArgumentCustomization = args => args - // this create the code coverage report - .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") + ArgumentCustomization = args => args + // this create the code coverage report + .Append("--collect:\"XPlat Code Coverage\"") }; EnsureDirectoryExists(artifactsForUnitTestsDir); DotNetCoreTest(unitTestAssemblies, testSettings); - var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); + var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.cobertura.xml")); Information(coverageSummaryFile); Information(artifactsForUnitTestsDir); @@ -215,8 +215,8 @@ Task("RunUnitTests") Information("We are not running on the build server so we won't publish the coverage report to coveralls.io"); } - var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage"); - var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage"); + var sequenceCoverage = XmlPeek(coverageSummaryFile, "//coverage/@line-rate"); + var branchCoverage = XmlPeek(coverageSummaryFile, "//coverage/@line-rate"); Information("Sequence Coverage: " + sequenceCoverage); diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 35aa633d4..a9ba3bfc9 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -89,7 +89,7 @@ - + all runtime; build; native; contentfiles; analyzers From ef69429dabdc20844c1c1a4c6dc6b75a955a2dc5 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 24 Nov 2020 08:18:09 +0000 Subject: [PATCH 13/34] mono complete --- docker/Dockerfile.base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 7b57dd23c..f635566e3 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -6,6 +6,6 @@ RUN apt update RUN apt install -y dirmngr gnupg apt-transport-https ca-certificates software-properties-common RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF RUN apt-add-repository 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' -RUN apt install -y mono-devel +RUN apt install -y mono-complete RUN apt install -y make RUN apt install -y build-essential From 730c940cd86dacc63c233ef48df290389d192c22 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 24 Nov 2020 08:18:30 +0000 Subject: [PATCH 14/34] wip --- .config/dotnet-tools.json | 2 +- .dockerignore | 7 +++++-- Makefile | 44 +++++++++++++++++++-------------------- build.cake | 6 +++--- build.ps1 | 2 +- build.sh | 5 +++-- docker/Dockerfile.base | 2 +- 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 73c839341..7c2d7fdc8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "cake.tool": { - "version": "0.38.5", + "version": "0.35.0", "commands": [ "dotnet-cake" ] diff --git a/.dockerignore b/.dockerignore index 54fec4f0a..ffb631235 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,5 @@ -*/*/bin -*/*/obj +*/*/bin +*/*/obj +tools/ +artifacts/ +TestResults/ \ No newline at end of file diff --git a/Makefile b/Makefile index 834ba760b..0dc3cf94a 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,23 @@ -NAME ?= ocelot - -build: - ./build.sh - -build_and_run_tests: - ./build.sh --target=RunTests - -release: - ./build.sh --target=Release - -run_acceptance_tests: - ./build.sh --target=RunAcceptanceTests - -run_benchmarks: - ./build.sh --target=RunBenchmarkTests - -run_unit_tests: - ./build.sh --target=RunUnitTests - -release_notes: - ./build.sh --target=ReleaseNotes +NAME ?= ocelot + +build: + mono --version + +build_and_run_tests: + ./build.sh --target=RunTests + +release: + ./build.sh --target=Release + +run_acceptance_tests: + ./build.sh --target=RunAcceptanceTests + +run_benchmarks: + ./build.sh --target=RunBenchmarkTests + +run_unit_tests: + ./build.sh --target=RunUnitTests + +release_notes: + ./build.sh --target=ReleaseNotes \ No newline at end of file diff --git a/build.cake b/build.cake index 6dda8a402..efd9e76b1 100644 --- a/build.cake +++ b/build.cake @@ -1,11 +1,11 @@ #tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" #addin nuget:?package=Cake.Json #addin nuget:?package=Newtonsoft.Json -#addin nuget:?package=System.Net.Http +#addin nuget:?package=System.Net.Http&version=4.3.4 #addin nuget:?package=System.Text.Encodings.Web&version=4.7.1 #tool "nuget:?package=ReportGenerator" -#tool "nuget:?package=coveralls.net&version=2.0.0" -#addin Cake.Coveralls&version=0.10.2 +#tool "nuget:?package=coveralls.net&version=0.7.0" +#addin Cake.Coveralls&version=0.10.1 // compile var compileConfig = Argument("configuration", "Release"); diff --git a/build.ps1 b/build.ps1 index 80bf4e64c..af696115f 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,2 +1,2 @@ -dotnet tool install Cake.Tool --version 0.38.5 +dotnet tool restore dotnet cake diff --git a/build.sh b/build.sh index 80bf4e64c..ad30a43a6 100755 --- a/build.sh +++ b/build.sh @@ -1,2 +1,3 @@ -dotnet tool install Cake.Tool --version 0.38.5 -dotnet cake +# dotnet tool restore --tool-manifest ./.config/dotnet-tools.json +# echo "tool installed?" +# dotnet cake diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 7b57dd23c..f635566e3 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -6,6 +6,6 @@ RUN apt update RUN apt install -y dirmngr gnupg apt-transport-https ca-certificates software-properties-common RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF RUN apt-add-repository 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' -RUN apt install -y mono-devel +RUN apt install -y mono-complete RUN apt install -y make RUN apt install -y build-essential From f4131028a32038cc488a8a5d20da5bf222be1fdf Mon Sep 17 00:00:00 2001 From: TomPallister Date: Wed, 25 Nov 2020 17:15:57 +0000 Subject: [PATCH 15/34] wip --- docker/Dockerfile.base | 45 +++++++++++++++++++++++++++++++++++------- docker/docker-build.sh | 4 ++-- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index f635566e3..692b281eb 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -1,11 +1,42 @@ # this is the dockerfile that create the ocelot build container # build with the docker-build.sh file in this folder -FROM mcr.microsoft.com/dotnet/sdk:5.0-focal AS build +FROM amd64/buildpack-deps:focal-curl AS build +# update to we can install wget RUN apt update -RUN apt install -y dirmngr gnupg apt-transport-https ca-certificates software-properties-common -RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF -RUN apt-add-repository 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' -RUN apt install -y mono-complete -RUN apt install -y make -RUN apt install -y build-essential + +# this need to be installed like this so it doesnt block the UI +RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata + +# bunch of random things .NET and mono need +RUN apt install -y wget apt-transport-https git make dirmngr gnupg apt-transport-https ca-certificates software-properties-common build-essential + +# install mono +RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && apt-add-repository 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' && apt install -y mono-complete + +# .NET Core dependencies +# RUN apt-get update \ +# && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ +# ca-certificates \ +# \ +# # .NET Core dependencies +# libc6 \ +# libgcc1 \ +# libgssapi-krb5-2 \ +# libicu66 \ +# libssl1.1 \ +# libstdc++6 \ +# zlib1g + +# Versions of .NET we need +RUN wget https://packages.microsoft.com/config/ubuntu/20.10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +RUN dpkg -i packages-microsoft-prod.deb +RUN apt-get update && apt-get install -y \ + dotnet-sdk-5.0 \ + dotnet-sdk-3.1 + +RUN dotnet tool install --global GitVersion.Tool --version 5.5.1 + +ENV PATH="/root/.dotnet/tools:${PATH}" + +# RUN dotnet gitversion \ No newline at end of file diff --git a/docker/docker-build.sh b/docker/docker-build.sh index 338a66b52..05a1c0cee 100755 --- a/docker/docker-build.sh +++ b/docker/docker-build.sh @@ -3,5 +3,5 @@ version=0.0.2 docker build -t mijitt0m/ocelot-build -f Dockerfile.base . echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:$version -docker push mijitt0m/ocelot-build:latest -docker push mijitt0m/ocelot-build:$version +# docker push mijitt0m/ocelot-build:latest +# docker push mijitt0m/ocelot-build:$version From 6e1dc3fd58364a36638e9693691fdf34e24790c8 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Wed, 25 Nov 2020 17:16:50 +0000 Subject: [PATCH 16/34] wip --- .config/dotnet-tools.json | 28 +++++++++++++++++----------- Makefile | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 7c2d7fdc8..f00d4981c 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -1,12 +1,18 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "cake.tool": { - "version": "0.35.0", - "commands": [ - "dotnet-cake" - ] - } - } +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "0.35.0", + "commands": [ + "dotnet-cake" + ] + }, + "gitversion.tool": { + "version": "5.5.1", + "commands": [ + "dotnet-gitversion" + ] + } + } } \ No newline at end of file diff --git a/Makefile b/Makefile index 0dc3cf94a..7f62b9782 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ NAME ?= ocelot build: - mono --version + dotnet tool restore --tool-manifest ./.config/dotnet-tools.json && dotnet cake --verbosity=diagnostic build_and_run_tests: ./build.sh --target=RunTests From 94ce0224d7176ba4de6ff65c7ad397fdf664b251 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Thu, 26 Nov 2020 11:03:38 +0000 Subject: [PATCH 17/34] wip --- Makefile | 12 ++++++++++ build.cake | 2 +- docker/Dockerfile.base | 50 ++++++++++-------------------------------- docker/docker-build.sh | 6 ++--- 4 files changed, 28 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index 7f62b9782..04a3eacab 100644 --- a/Makefile +++ b/Makefile @@ -20,4 +20,16 @@ run_unit_tests: release_notes: ./build.sh --target=ReleaseNotes + +# clean the dirs +# version the code and update the csproj files +# write the release notes +# build the code +# unit test +# acceptance test +# integration test +# gather the nuget packages +# publish release to NuGet +# publish release to GitHub + \ No newline at end of file diff --git a/build.cake b/build.cake index efd9e76b1..592e9858e 100644 --- a/build.cake +++ b/build.cake @@ -1,4 +1,4 @@ -#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" +#tool "nuget:?package=GitVersion.CommandLine&version=5.5.1" #addin nuget:?package=Cake.Json #addin nuget:?package=Newtonsoft.Json #addin nuget:?package=System.Net.Http&version=4.3.4 diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 692b281eb..cc6798c37 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -1,42 +1,16 @@ # this is the dockerfile that create the ocelot build container # build with the docker-build.sh file in this folder -FROM amd64/buildpack-deps:focal-curl AS build +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build -# update to we can install wget +RUN apt install gnupg ca-certificates +RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF +RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list RUN apt update - -# this need to be installed like this so it doesnt block the UI -RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata - -# bunch of random things .NET and mono need -RUN apt install -y wget apt-transport-https git make dirmngr gnupg apt-transport-https ca-certificates software-properties-common build-essential - -# install mono -RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && apt-add-repository 'deb https://download.mono-project.com/repo/ubuntu stable-focal main' && apt install -y mono-complete - -# .NET Core dependencies -# RUN apt-get update \ -# && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ -# ca-certificates \ -# \ -# # .NET Core dependencies -# libc6 \ -# libgcc1 \ -# libgssapi-krb5-2 \ -# libicu66 \ -# libssl1.1 \ -# libstdc++6 \ -# zlib1g - -# Versions of .NET we need -RUN wget https://packages.microsoft.com/config/ubuntu/20.10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb -RUN dpkg -i packages-microsoft-prod.deb -RUN apt-get update && apt-get install -y \ - dotnet-sdk-5.0 \ - dotnet-sdk-3.1 - -RUN dotnet tool install --global GitVersion.Tool --version 5.5.1 - -ENV PATH="/root/.dotnet/tools:${PATH}" - -# RUN dotnet gitversion \ No newline at end of file +RUN apt-get -y install mono-devel + +RUN wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \ + && dpkg -i packages-microsoft-prod.deb \ + && apt-get update; \ + apt-get install -y apt-transport-https && \ + apt-get update && \ + apt-get install -y dotnet-sdk-5.0 \ No newline at end of file diff --git a/docker/docker-build.sh b/docker/docker-build.sh index 05a1c0cee..d2d639347 100755 --- a/docker/docker-build.sh +++ b/docker/docker-build.sh @@ -1,7 +1,7 @@ # this script build the ocelot docker file -version=0.0.2 +version=0.0.3 docker build -t mijitt0m/ocelot-build -f Dockerfile.base . echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin docker tag mijitt0m/ocelot-build mijitt0m/ocelot-build:$version -# docker push mijitt0m/ocelot-build:latest -# docker push mijitt0m/ocelot-build:$version +docker push mijitt0m/ocelot-build:latest +docker push mijitt0m/ocelot-build:$version From de0f4f97d278fb100eea75a5917f46078c294be2 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Thu, 26 Nov 2020 12:52:53 +0000 Subject: [PATCH 18/34] build finally working....MAGIC --- Makefile | 2 +- build.cake | 2 +- build.ps1 | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++- build.sh | 120 ++++++++++++++++++++++++- 4 files changed, 375 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 04a3eacab..bcf8e8951 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ NAME ?= ocelot build: - dotnet tool restore --tool-manifest ./.config/dotnet-tools.json && dotnet cake --verbosity=diagnostic + ./build.sh build_and_run_tests: ./build.sh --target=RunTests diff --git a/build.cake b/build.cake index 592e9858e..efd9e76b1 100644 --- a/build.cake +++ b/build.cake @@ -1,4 +1,4 @@ -#tool "nuget:?package=GitVersion.CommandLine&version=5.5.1" +#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" #addin nuget:?package=Cake.Json #addin nuget:?package=Newtonsoft.Json #addin nuget:?package=System.Net.Http&version=4.3.4 diff --git a/build.ps1 b/build.ps1 index af696115f..a336e2985 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,2 +1,256 @@ -dotnet tool restore -dotnet cake +########################################################################## +# This is the Cake bootstrapper script for PowerShell. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +<# + +.SYNOPSIS +This is a Powershell script to bootstrap a Cake build. + +.DESCRIPTION +This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) +and execute your Cake build script with the parameters you provide. + +.PARAMETER Script +The build script to execute. +.PARAMETER Target +The build script target to run. +.PARAMETER Configuration +The build configuration to use. +.PARAMETER Verbosity +Specifies the amount of information to be displayed. +.PARAMETER ShowDescription +Shows description about tasks. +.PARAMETER DryRun +Performs a dry run. +.PARAMETER SkipToolPackageRestore +Skips restoring of packages. +.PARAMETER ScriptArgs +Remaining arguments are added here. + +.LINK +https://cakebuild.net + +#> + +[CmdletBinding()] +Param( + [string]$Script = "build.cake", + [string]$Target, + [string]$Configuration, + [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] + [string]$Verbosity, + [switch]$ShowDescription, + [Alias("WhatIf", "Noop")] + [switch]$DryRun, + [switch]$SkipToolPackageRestore, + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$ScriptArgs +) + +# Attempt to set highest encryption available for SecurityProtocol. +# PowerShell will not set this by default (until maybe .NET 4.6.x). This +# will typically produce a message for PowerShell v2 (just an info +# message though) +try { + # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) + # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't + # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is + # installed (.NET 4.5 is an in-place upgrade). + # PowerShell Core already has support for TLS 1.2 so we can skip this if running in that. + if (-not $IsCoreCLR) { + [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + } + } catch { + Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' + } + +[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null +function MD5HashFile([string] $filePath) +{ + if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) + { + return $null + } + + [System.IO.Stream] $file = $null; + [System.Security.Cryptography.MD5] $md5 = $null; + try + { + $md5 = [System.Security.Cryptography.MD5]::Create() + $file = [System.IO.File]::OpenRead($filePath) + return [System.BitConverter]::ToString($md5.ComputeHash($file)) + } + finally + { + if ($file -ne $null) + { + $file.Dispose() + } + } +} + +function GetProxyEnabledWebClient +{ + $wc = New-Object System.Net.WebClient + $proxy = [System.Net.WebRequest]::GetSystemWebProxy() + $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $wc.Proxy = $proxy + return $wc +} + +Write-Host "Preparing to run build script..." + +if(!$PSScriptRoot){ + $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent +} + +$TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" +$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" +$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" +$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" +$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" +$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" +$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" + +# Make sure tools folder exists +if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { + Write-Verbose -Message "Creating tools directory..." + New-Item -Path $TOOLS_DIR -Type Directory | Out-Null +} + +# Make sure that packages.config exist. +if (!(Test-Path $PACKAGES_CONFIG)) { + Write-Verbose -Message "Downloading packages.config..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) + } catch { + Throw "Could not download packages.config." + } +} + +# Try find NuGet.exe in path if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Trying to find nuget.exe in PATH..." + $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } + $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 + if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { + Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." + $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName + } +} + +# Try download NuGet.exe if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Downloading NuGet.exe..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile($NUGET_URL, $NUGET_EXE) + } catch { + Throw "Could not download NuGet.exe." + } +} + +# Save nuget.exe path to environment to be available to child processed +$env:NUGET_EXE = $NUGET_EXE +$env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { + "mono `"$NUGET_EXE`"" +} else { + "`"$NUGET_EXE`"" +} + +# Restore tools from NuGet? +if(-Not $SkipToolPackageRestore.IsPresent) { + Push-Location + Set-Location $TOOLS_DIR + + # Check for changes in packages.config and remove installed tools if true. + [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG + if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or + ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { + Write-Verbose -Message "Missing or changed package.config hash..." + Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | + Remove-Item -Recurse -Force + } + + Write-Verbose -Message "Restoring tools from NuGet..." + + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet tools." + } + else + { + $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" + } + Write-Verbose -Message ($NuGetOutput | Out-String) + + Pop-Location +} + +# Restore addins from NuGet +if (Test-Path $ADDINS_PACKAGES_CONFIG) { + Push-Location + Set-Location $ADDINS_DIR + + Write-Verbose -Message "Restoring addins from NuGet..." + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet addins." + } + + Write-Verbose -Message ($NuGetOutput | Out-String) + + Pop-Location +} + +# Restore modules from NuGet +if (Test-Path $MODULES_PACKAGES_CONFIG) { + Push-Location + Set-Location $MODULES_DIR + + Write-Verbose -Message "Restoring modules from NuGet..." + $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet modules." + } + + Write-Verbose -Message ($NuGetOutput | Out-String) + + Pop-Location +} + +# Make sure that Cake has been installed. +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find Cake.exe at $CAKE_EXE" +} + +$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { + "mono `"$CAKE_EXE`"" +} else { + "`"$CAKE_EXE`"" +} + + # Build an array (not a string) of Cake arguments to be joined later +$cakeArguments = @() +if ($Script) { $cakeArguments += "`"$Script`"" } +if ($Target) { $cakeArguments += "-target=`"$Target`"" } +if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } +if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } +if ($ShowDescription) { $cakeArguments += "-showdescription" } +if ($DryRun) { $cakeArguments += "-dryrun" } +$cakeArguments += $ScriptArgs + +# Start Cake +Write-Host "Running build script..." +Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")" +exit $LASTEXITCODE diff --git a/build.sh b/build.sh index ad30a43a6..b9e12527f 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,117 @@ -# dotnet tool restore --tool-manifest ./.config/dotnet-tools.json -# echo "tool installed?" -# dotnet cake +#!/usr/bin/env bash + +########################################################################## +# This is the Cake bootstrapper script for Linux and OS X. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +# Define directories. +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +TOOLS_DIR=$SCRIPT_DIR/tools +ADDINS_DIR=$TOOLS_DIR/Addins +MODULES_DIR=$TOOLS_DIR/Modules +NUGET_EXE=$TOOLS_DIR/nuget.exe +CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe +PACKAGES_CONFIG=$TOOLS_DIR/packages.config +PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum +ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config +MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config + +# Define md5sum or md5 depending on Linux/OSX +MD5_EXE= +if [[ "$(uname -s)" == "Darwin" ]]; then + MD5_EXE="md5 -r" +else + MD5_EXE="md5sum" +fi + +# Define default arguments. +SCRIPT="build.cake" +CAKE_ARGUMENTS=() + +# Parse arguments. +for i in "$@"; do + case $1 in + -s|--script) SCRIPT="$2"; shift ;; + --) shift; CAKE_ARGUMENTS+=("$@"); break ;; + *) CAKE_ARGUMENTS+=("$1") ;; + esac + shift +done + +# Make sure the tools folder exist. +if [ ! -d "$TOOLS_DIR" ]; then + mkdir "$TOOLS_DIR" +fi + +# Make sure that packages.config exist. +if [ ! -f "$TOOLS_DIR/packages.config" ]; then + echo "Downloading packages.config..." + curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages + if [ $? -ne 0 ]; then + echo "An error occurred while downloading packages.config." + exit 1 + fi +fi + +# Download NuGet if it does not exist. +if [ ! -f "$NUGET_EXE" ]; then + echo "Downloading NuGet..." + curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe + if [ $? -ne 0 ]; then + echo "An error occurred while downloading nuget.exe." + exit 1 + fi +fi + +# Restore tools from NuGet. +pushd "$TOOLS_DIR" >/dev/null +if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then + find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf +fi + +mono "$NUGET_EXE" install -ExcludeVersion +if [ $? -ne 0 ]; then + echo "Could not restore NuGet tools." + exit 1 +fi + +$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" + +popd >/dev/null + +# Restore addins from NuGet. +if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then + pushd "$ADDINS_DIR" >/dev/null + + mono "$NUGET_EXE" install -ExcludeVersion + if [ $? -ne 0 ]; then + echo "Could not restore NuGet addins." + exit 1 + fi + + popd >/dev/null +fi + +# Restore modules from NuGet. +if [ -f "$MODULES_PACKAGES_CONFIG" ]; then + pushd "$MODULES_DIR" >/dev/null + + mono "$NUGET_EXE" install -ExcludeVersion + if [ $? -ne 0 ]; then + echo "Could not restore NuGet modules." + exit 1 + fi + + popd >/dev/null +fi + +# Make sure that Cake has been installed. +if [ ! -f "$CAKE_EXE" ]; then + echo "Could not find Cake.exe at '$CAKE_EXE'." + exit 1 +fi + +# Start Cake +exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}" From 03c8cd6a9d323fcba094de3840ce695c4a5c680d Mon Sep 17 00:00:00 2001 From: TomPallister Date: Thu, 26 Nov 2020 15:25:13 +0000 Subject: [PATCH 19/34] works on linux --- docker/Dockerfile.build | 2 +- docker/Dockerfile.release | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile.build b/docker/Dockerfile.build index 057d0a9ba..4b99c053c 100644 --- a/docker/Dockerfile.build +++ b/docker/Dockerfile.build @@ -1,6 +1,6 @@ # call from ocelot repo root with # docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.build . -FROM mijitt0m/ocelot-build:0.0.2 +FROM mijitt0m/ocelot-build:0.0.3 ARG OCELOT_COVERALLS_TOKEN diff --git a/docker/Dockerfile.release b/docker/Dockerfile.release index 1a7a4676b..0ce0e95ea 100644 --- a/docker/Dockerfile.release +++ b/docker/Dockerfile.release @@ -1,7 +1,7 @@ # call from ocelot repo root with # docker build --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN --build-arg OCELOT_GITHUB_API_KEY=$OCELOT_GITHUB_API_KEY --build-arg OCELOT_COVERALLS_TOKEN=$OCELOT_COVERALLS_TOKEN -f ./docker/Dockerfile.release . -FROM mijitt0m/ocelot-build:0.0.2 +FROM mijitt0m/ocelot-build:0.0.3 ARG OCELOT_COVERALLS_TOKEN ARG OCELOT_NUTGET_API_KEY From bdefa2de54b1919888b8d59791af84d45cb8dc9e Mon Sep 17 00:00:00 2001 From: TomPallister Date: Thu, 26 Nov 2020 16:45:57 +0000 Subject: [PATCH 20/34] dont need .config --- .config/dotnet-tools.json | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .config/dotnet-tools.json diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json deleted file mode 100644 index f00d4981c..000000000 --- a/.config/dotnet-tools.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "cake.tool": { - "version": "0.35.0", - "commands": [ - "dotnet-cake" - ] - }, - "gitversion.tool": { - "version": "5.5.1", - "commands": [ - "dotnet-gitversion" - ] - } - } -} \ No newline at end of file From a39b5fd5b433e24da6042858f137cc9ef11b2acb Mon Sep 17 00:00:00 2001 From: TomPallister Date: Thu, 26 Nov 2020 16:48:11 +0000 Subject: [PATCH 21/34] dont need .config --- Makefile | 11 ----------- Ocelot.sln | 1 - 2 files changed, 12 deletions(-) diff --git a/Makefile b/Makefile index bcf8e8951..a83aa708e 100644 --- a/Makefile +++ b/Makefile @@ -20,16 +20,5 @@ run_unit_tests: release_notes: ./build.sh --target=ReleaseNotes - -# clean the dirs -# version the code and update the csproj files -# write the release notes -# build the code -# unit test -# acceptance test -# integration test -# gather the nuget packages -# publish release to NuGet -# publish release to GitHub \ No newline at end of file diff --git a/Ocelot.sln b/Ocelot.sln index 922f62261..511c79839 100644 --- a/Ocelot.sln +++ b/Ocelot.sln @@ -12,7 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore build.cake = build.cake build.ps1 = build.ps1 - build.sh = build.sh codeanalysis.ruleset = codeanalysis.ruleset .circleci\config.yml = .circleci\config.yml GitVersion.yml = GitVersion.yml From 9bc5ad1cccebe7d98a2c7845135faa6176e6aa94 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Thu, 26 Nov 2020 16:51:37 +0000 Subject: [PATCH 22/34] dont need this --- .dockerignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index ffb631235..43f01f6dc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,4 @@ */*/bin */*/obj -tools/ artifacts/ TestResults/ \ No newline at end of file From fdad2bb3021fd6ea91aeccd18138e06b7eb4bed5 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Thu, 26 Nov 2020 16:53:42 +0000 Subject: [PATCH 23/34] update circle build image --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3325b02df..e38eb5ab7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,27 +1,27 @@ -version: 2.1 +version: 2.1 orbs: queue: eddiewebb/queue@1.5.0 jobs: build: docker: - - image: mijitt0m/ocelot-build:0.0.1 + - image: mijitt0m/ocelot-build:0.0.3 steps: - checkout - run: make build release: docker: - - image: mijitt0m/ocelot-build:0.0.1 + - image: mijitt0m/ocelot-build:0.0.3 steps: - checkout - run: make release workflows: version: 2 master: - jobs: + jobs: - queue/block_workflow: time: "20" only-on-branch: master - - release: + - release: requires: - queue/block_workflow filters: From b356539cbc24c590ea417fc393c80c59d833c183 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 27 Nov 2020 11:55:19 +0000 Subject: [PATCH 24/34] update nuget packages and graphql example --- samples/OcelotGraphQL/OcelotGraphQL.csproj | 2 +- samples/OcelotGraphQL/Program.cs | 263 +++++++++--------- .../Ocelot.Provider.Consul.csproj | 2 +- src/Ocelot/Ocelot.csproj | 2 +- src/Ocelot/Responses/Response.cs | 38 +-- .../Ocelot.AcceptanceTests.csproj | 2 +- .../Ocelot.Benchmarks.csproj | 2 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 2 +- 8 files changed, 159 insertions(+), 154 deletions(-) diff --git a/samples/OcelotGraphQL/OcelotGraphQL.csproj b/samples/OcelotGraphQL/OcelotGraphQL.csproj index 271ae56fa..f205c25e3 100644 --- a/samples/OcelotGraphQL/OcelotGraphQL.csproj +++ b/samples/OcelotGraphQL/OcelotGraphQL.csproj @@ -12,6 +12,6 @@ - + diff --git a/samples/OcelotGraphQL/Program.cs b/samples/OcelotGraphQL/Program.cs index a18240572..ef23d98d4 100644 --- a/samples/OcelotGraphQL/Program.cs +++ b/samples/OcelotGraphQL/Program.cs @@ -1,133 +1,138 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Ocelot.Middleware; -using Ocelot.DependencyInjection; -using GraphQL.Types; -using GraphQL; -using Ocelot.Requester; -using Ocelot.Responses; -using System.Net.Http; -using System.Net; -using Microsoft.Extensions.DependencyInjection; -using System.Threading; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Ocelot.Middleware; +using Ocelot.DependencyInjection; +using GraphQL.Types; +using GraphQL; +using Ocelot.Requester; +using Ocelot.Responses; +using System.Net.Http; +using System.Net; +using Microsoft.Extensions.DependencyInjection; +using System.Threading; + +namespace OcelotGraphQL +{ + public class Hero + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class Query + { + private readonly List _heroes = new List + { + new Hero { Id = 1, Name = "R2-D2" }, + new Hero { Id = 2, Name = "Batman" }, + new Hero { Id = 3, Name = "Wonder Woman" }, + new Hero { Id = 4, Name = "Tom Pallister" } + }; + + [GraphQLMetadata("hero")] + public Hero GetHero(int id) + { + return _heroes.FirstOrDefault(x => x.Id == id); + } + } + + public class GraphQlDelegatingHandler : DelegatingHandler + { + //private readonly ISchema _schema; + private readonly IDocumentExecuter _executer; + private readonly IDocumentWriter _writer; + + public GraphQlDelegatingHandler(IDocumentExecuter executer, IDocumentWriter writer) + { + _executer = executer; + _writer = writer; + } + + protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + //try get query from body, could check http method :) + var query = await request.Content.ReadAsStringAsync(); + + //if not body try query string, dont hack like this in real world.. + if (query.Length == 0) + { + var decoded = WebUtility.UrlDecode(request.RequestUri.Query); + query = decoded.Replace("?query=", ""); + } -namespace OcelotGraphQL -{ - public class Hero - { - public int Id { get; set; } - public string Name { get; set; } - } - - public class Query - { - private readonly List _heroes = new List - { - new Hero { Id = 1, Name = "R2-D2" }, - new Hero { Id = 2, Name = "Batman" }, - new Hero { Id = 3, Name = "Wonder Woman" }, - new Hero { Id = 4, Name = "Tom Pallister" } - }; - - [GraphQLMetadata("hero")] - public Hero GetHero(int id) - { - return _heroes.FirstOrDefault(x => x.Id == id); - } - } - - public class GraphQlDelegatingHandler : DelegatingHandler - { - private readonly ISchema _schema; - - public GraphQlDelegatingHandler(ISchema schema) - { - _schema = schema; - } - - protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - //try get query from body, could check http method :) - var query = await request.Content.ReadAsStringAsync(); - - //if not body try query string, dont hack like this in real world.. - if (query.Length == 0) - { - var decoded = WebUtility.UrlDecode(request.RequestUri.Query); - query = decoded.Replace("?query=", ""); - } - - var result = _schema.Execute(_ => - { - _.Query = query; + var result = await _executer.ExecuteAsync(_ => + { + _.Query = query; }); - //maybe check for errors and headers etc in real world? - var response = new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent(result) - }; - - //ocelot will treat this like any other http request... - return response; - } - } - - public class Program - { - public static void Main() - { - var schema = Schema.For(@" - type Hero { - id: Int - name: String - } - - type Query { - hero(id: Int): Hero - } - ", _ => - { - _.Types.Include(); - }); + var responseBody = await _writer.WriteToStringAsync(result); - new WebHostBuilder() - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config - .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) - .AddJsonFile("ocelot.json", false, false) - .AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddSingleton(schema); - s.AddOcelot() - .AddDelegatingHandler(); - }) - .ConfigureLogging((hostingContext, logging) => - { - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); - }) - .UseIISIntegration() - .Configure(app => - { - app.UseOcelot().Wait(); - }) - .Build() - .Run(); - } - } -} + //maybe check for errors and headers etc in real world? + var response = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(responseBody) + }; + + //ocelot will treat this like any other http request... + return response; + } + } + + public class Program + { + public static void Main() + { + var schema = Schema.For(@" + type Hero { + id: Int + name: String + } + + type Query { + hero(id: Int): Hero + } + ", _ => + { + _.Types.Include(); + }); + + new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config + .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) + .AddJsonFile("ocelot.json", false, false) + .AddEnvironmentVariables(); + }) + .ConfigureServices(s => + { + s.AddSingleton(schema); + s.AddOcelot() + .AddDelegatingHandler(); + }) + .ConfigureLogging((hostingContext, logging) => + { + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + logging.AddConsole(); + }) + .UseIISIntegration() + .Configure(app => + { + app.UseOcelot().Wait(); + }) + .Build() + .Run(); + } + } +} diff --git a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj index 88a6afc33..13472b026 100644 --- a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj +++ b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj @@ -27,7 +27,7 @@ - + all diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index 1481b83c8..f925df8a1 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -24,7 +24,7 @@ - + NU1701 diff --git a/src/Ocelot/Responses/Response.cs b/src/Ocelot/Responses/Response.cs index a10d480b9..7158f010a 100644 --- a/src/Ocelot/Responses/Response.cs +++ b/src/Ocelot/Responses/Response.cs @@ -1,22 +1,22 @@ -using Ocelot.Errors; -using System.Collections.Generic; - -namespace Ocelot.Responses -{ - public abstract class Response - { - protected Response() - { - Errors = new List(); - } - - protected Response(List errors) - { - Errors = errors ?? new List(); +using Ocelot.Errors; +using System.Collections.Generic; + +namespace Ocelot.Responses +{ + public abstract class Response + { + protected Response() + { + Errors = new List(); + } + + protected Response(List errors) + { + Errors = errors ?? new List(); } - public List Errors { get; } - - public bool IsError => Errors.Count > 0; - } + public List Errors { get; } + + public bool IsError => Errors.Count > 0; + } } diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 2552ec47f..193ce9db5 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -65,7 +65,7 @@ - + diff --git a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj index cd3c62b76..99d9b0c60 100644 --- a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj +++ b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj @@ -18,7 +18,7 @@ - + all diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index a9ba3bfc9..439d72f30 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -75,7 +75,7 @@ - + From ebf85326cf4d9eadba74729ab1a708f538908348 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 27 Nov 2020 12:41:01 +0000 Subject: [PATCH 25/34] removed rafty and updated more packages --- Ocelot.sln | 7 - docs/features/raft.rst | 49 -- docs/index.rst | 1 - samples/Docker/Dockerfile | 1 - .../OcelotKube/ApiGateway/ApiGateway.csproj | 2 +- .../DownstreamService.csproj | 2 +- .../OcelotOpenTracing.csproj | 4 +- .../OcelotApplicationApiGateway.csproj | 4 +- .../OcelotApplicationService.csproj | 6 +- .../Ocelot.Provider.Kubernetes.csproj | 4 +- src/Ocelot.Provider.Rafty/BearerToken.cs | 16 - src/Ocelot.Provider.Rafty/FakeCommand.cs | 14 - src/Ocelot.Provider.Rafty/FilePeer.cs | 7 - src/Ocelot.Provider.Rafty/FilePeers.cs | 14 - .../FilePeersProvider.cs | 44 -- src/Ocelot.Provider.Rafty/HttpPeer.cs | 130 ----- .../Ocelot.Provider.Rafty.csproj | 40 -- .../OcelotAdministrationBuilderExtensions.cs | 28 - .../OcelotFiniteStateMachine.cs | 25 - .../Properties/AssemblyInfo.cs | 18 - src/Ocelot.Provider.Rafty/RaftController.cs | 96 ---- .../RaftyFileConfigurationSetter.cs | 30 - .../RaftyMiddlewareConfigurationProvider.cs | 49 -- src/Ocelot.Provider.Rafty/SqlLiteLog.cs | 334 ------------ .../UnableToSaveAcceptCommand.cs | 12 - .../UpdateFileConfiguration.cs | 15 - src/Ocelot/Ocelot.csproj | 6 +- .../ButterflyTracingTests.cs | 3 +- test/Ocelot.AcceptanceTests/ContentTests.cs | 2 +- .../Ocelot.AcceptanceTests.csproj | 19 +- .../OpenTracingTests.cs | 69 ++- .../Ocelot.IntegrationTests.csproj | 119 ++-- test/Ocelot.IntegrationTests/RaftTests.cs | 513 ------------------ .../Ocelot.ManualTest.csproj | 14 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 18 +- ...lotAdministrationBuilderExtensionsTests.cs | 89 --- .../Rafty/OcelotFiniteStateMachineTests.cs | 45 -- .../RaftyFileConfigurationSetterTests.cs | 52 -- 38 files changed, 167 insertions(+), 1734 deletions(-) delete mode 100644 docs/features/raft.rst delete mode 100644 src/Ocelot.Provider.Rafty/BearerToken.cs delete mode 100644 src/Ocelot.Provider.Rafty/FakeCommand.cs delete mode 100644 src/Ocelot.Provider.Rafty/FilePeer.cs delete mode 100644 src/Ocelot.Provider.Rafty/FilePeers.cs delete mode 100644 src/Ocelot.Provider.Rafty/FilePeersProvider.cs delete mode 100644 src/Ocelot.Provider.Rafty/HttpPeer.cs delete mode 100644 src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj delete mode 100644 src/Ocelot.Provider.Rafty/OcelotAdministrationBuilderExtensions.cs delete mode 100644 src/Ocelot.Provider.Rafty/OcelotFiniteStateMachine.cs delete mode 100644 src/Ocelot.Provider.Rafty/Properties/AssemblyInfo.cs delete mode 100644 src/Ocelot.Provider.Rafty/RaftController.cs delete mode 100644 src/Ocelot.Provider.Rafty/RaftyFileConfigurationSetter.cs delete mode 100644 src/Ocelot.Provider.Rafty/RaftyMiddlewareConfigurationProvider.cs delete mode 100644 src/Ocelot.Provider.Rafty/SqlLiteLog.cs delete mode 100644 src/Ocelot.Provider.Rafty/UnableToSaveAcceptCommand.cs delete mode 100644 src/Ocelot.Provider.Rafty/UpdateFileConfiguration.cs delete mode 100644 test/Ocelot.IntegrationTests/RaftTests.cs delete mode 100644 test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs delete mode 100644 test/Ocelot.UnitTests/Rafty/OcelotFiniteStateMachineTests.cs delete mode 100644 test/Ocelot.UnitTests/Rafty/RaftyFileConfigurationSetterTests.cs diff --git a/Ocelot.sln b/Ocelot.sln index 511c79839..ade57b25f 100644 --- a/Ocelot.sln +++ b/Ocelot.sln @@ -44,8 +44,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Eureka", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Polly", "src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj", "{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj", "{AC153C67-EF18-47E6-A230-F0D3CF5F0A98}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}" @@ -138,10 +136,6 @@ Global {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E}.Release|Any CPU.Build.0 = Release|Any CPU - {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC153C67-EF18-47E6-A230-F0D3CF5F0A98}.Release|Any CPU.Build.0 = Release|Any CPU {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU {6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -210,7 +204,6 @@ Global {02F5AE4D-9C36-4E58-B7C6-012CBBDEFDE0} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {9BBD3586-145C-4FA0-91C5-9ED58287D753} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} - {AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {ED0B3A09-112B-4BA4-82D6-11569BC7A99B} = {ED066001-BAF7-4117-9884-DF591A56347D} diff --git a/docs/features/raft.rst b/docs/features/raft.rst deleted file mode 100644 index 69b804043..000000000 --- a/docs/features/raft.rst +++ /dev/null @@ -1,49 +0,0 @@ -Raft (EXPERIMENTAL DO NOT USE IN PRODUCTION) -============================================ - -Ocelot has recently integrated `Rafty `_ which is an implementation of Raft that I have also been working on over the last year. This project is very experimental so please do not use this feature of Ocelot in production until I think it's OK. - -Raft is a distributed concensus algorithm that allows a cluster of servers (Ocelots) to maintain local state without having a centralised database for storing state (e.g. SQL Server). - -To get Raft support you must first install the Ocelot Rafty package. - -``Install-Package Ocelot.Provider.Rafty`` - -Then you must make the following changes to your Startup.cs / Program.cs. - -.. code-block:: csharp - - public virtual void ConfigureServices(IServiceCollection services) - { - services - .AddOcelot() - .AddAdministration("/administration", "secret") - .AddRafty(); - } - -In addition to this you must add a file called peers.json to your main project and it will look as follows - -.. code-block:: json - - { - "Peers": [{ - "HostAndPort": "http://localhost:5000" - }, - { - "HostAndPort": "http://localhost:5002" - }, - { - "HostAndPort": "http://localhost:5003" - }, - { - "HostAndPort": "http://localhost:5004" - }, - { - "HostAndPort": "http://localhost:5001" - } - ] - } - -Each instance of Ocelot must have it's address in the array so that they can communicate using Rafty. - -Once you have made these configuration changes you must deploy and start each instance of Ocelot using the addresses in the peers.json file. The servers should then start communicating with each other! You can test if everything is working by posting a configuration update and checking it has replicated to all servers by getting their configuration. diff --git a/docs/index.rst b/docs/index.rst index 1acbd7124..44ad05c35 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,7 +41,6 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n features/middlewareinjection features/loadbalancer features/delegatinghandlers - features/raft features/errorcodes .. toctree:: diff --git a/samples/Docker/Dockerfile b/samples/Docker/Dockerfile index 86b12ab01..dad54b20f 100644 --- a/samples/Docker/Dockerfile +++ b/samples/Docker/Dockerfile @@ -17,7 +17,6 @@ COPY src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj src/Ocelot.C COPY src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj COPY src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj COPY src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj -COPY src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj COPY src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj COPY src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj COPY test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj diff --git a/samples/OcelotKube/ApiGateway/ApiGateway.csproj b/samples/OcelotKube/ApiGateway/ApiGateway.csproj index e4c39f28f..a96c220ee 100644 --- a/samples/OcelotKube/ApiGateway/ApiGateway.csproj +++ b/samples/OcelotKube/ApiGateway/ApiGateway.csproj @@ -7,7 +7,7 @@ - + diff --git a/samples/OcelotKube/DownstreamService/DownstreamService.csproj b/samples/OcelotKube/DownstreamService/DownstreamService.csproj index 3dc2bee23..732c6594b 100644 --- a/samples/OcelotKube/DownstreamService/DownstreamService.csproj +++ b/samples/OcelotKube/DownstreamService/DownstreamService.csproj @@ -7,7 +7,7 @@ - + diff --git a/samples/OcelotOpenTracing/OcelotOpenTracing.csproj b/samples/OcelotOpenTracing/OcelotOpenTracing.csproj index 93279c7b0..fad9470ea 100644 --- a/samples/OcelotOpenTracing/OcelotOpenTracing.csproj +++ b/samples/OcelotOpenTracing/OcelotOpenTracing.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj index c2f7c8a1e..f0fd14d40 100644 --- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj +++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/OcelotApplicationApiGateway.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj index a286dd790..b7df220e4 100644 --- a/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj +++ b/samples/OcelotServiceFabric/src/OcelotApplicationService/OcelotApplicationService.csproj @@ -9,10 +9,10 @@ $(PackageTargetFallback) - - + + - + diff --git a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj index a2de9ea89..c8d1ebde1 100644 --- a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj +++ b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj @@ -28,8 +28,8 @@ - - + + diff --git a/src/Ocelot.Provider.Rafty/BearerToken.cs b/src/Ocelot.Provider.Rafty/BearerToken.cs deleted file mode 100644 index c006aaf3a..000000000 --- a/src/Ocelot.Provider.Rafty/BearerToken.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Newtonsoft.Json; - - internal class BearerToken - { - [JsonProperty("access_token")] - public string AccessToken { get; set; } - - [JsonProperty("expires_in")] - public int ExpiresIn { get; set; } - - [JsonProperty("token_type")] - public string TokenType { get; set; } - } -} diff --git a/src/Ocelot.Provider.Rafty/FakeCommand.cs b/src/Ocelot.Provider.Rafty/FakeCommand.cs deleted file mode 100644 index de611da74..000000000 --- a/src/Ocelot.Provider.Rafty/FakeCommand.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using global::Rafty.FiniteStateMachine; - - public class FakeCommand : ICommand - { - public FakeCommand(string value) - { - this.Value = value; - } - - public string Value { get; private set; } - } -} diff --git a/src/Ocelot.Provider.Rafty/FilePeer.cs b/src/Ocelot.Provider.Rafty/FilePeer.cs deleted file mode 100644 index 4bb57548c..000000000 --- a/src/Ocelot.Provider.Rafty/FilePeer.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - public class FilePeer - { - public string HostAndPort { get; set; } - } -} diff --git a/src/Ocelot.Provider.Rafty/FilePeers.cs b/src/Ocelot.Provider.Rafty/FilePeers.cs deleted file mode 100644 index 4d5f9e39e..000000000 --- a/src/Ocelot.Provider.Rafty/FilePeers.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using System.Collections.Generic; - - public class FilePeers - { - public FilePeers() - { - Peers = new List(); - } - - public List Peers { get; set; } - } -} diff --git a/src/Ocelot.Provider.Rafty/FilePeersProvider.cs b/src/Ocelot.Provider.Rafty/FilePeersProvider.cs deleted file mode 100644 index ddca1ac2c..000000000 --- a/src/Ocelot.Provider.Rafty/FilePeersProvider.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Administration; - using Configuration.Repository; - using global::Rafty.Concensus.Peers; - using global::Rafty.Infrastructure; - using Microsoft.Extensions.Options; - using Middleware; - using System.Collections.Generic; - using System.Net.Http; - - public class FilePeersProvider : IPeersProvider - { - private readonly IOptions _options; - private readonly List _peers; - private IBaseUrlFinder _finder; - private IInternalConfigurationRepository _repo; - private IIdentityServerConfiguration _identityServerConfig; - - public FilePeersProvider(IOptions options, IBaseUrlFinder finder, IInternalConfigurationRepository repo, IIdentityServerConfiguration identityServerConfig) - { - _identityServerConfig = identityServerConfig; - _repo = repo; - _finder = finder; - _options = options; - _peers = new List(); - - var config = _repo.Get(); - foreach (var item in _options.Value.Peers) - { - var httpClient = new HttpClient(); - - //todo what if this errors? - var httpPeer = new HttpPeer(item.HostAndPort, httpClient, _finder, config.Data, _identityServerConfig); - _peers.Add(httpPeer); - } - } - - public List Get() - { - return _peers; - } - } -} diff --git a/src/Ocelot.Provider.Rafty/HttpPeer.cs b/src/Ocelot.Provider.Rafty/HttpPeer.cs deleted file mode 100644 index c3ed9e658..000000000 --- a/src/Ocelot.Provider.Rafty/HttpPeer.cs +++ /dev/null @@ -1,130 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Administration; - using Configuration; - using global::Rafty.Concensus.Messages; - using global::Rafty.Concensus.Peers; - using global::Rafty.FiniteStateMachine; - using global::Rafty.Infrastructure; - using Middleware; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.Net.Http; - using System.Threading.Tasks; - - public class HttpPeer : IPeer - { - private readonly string _hostAndPort; - private readonly HttpClient _httpClient; - private readonly JsonSerializerSettings _jsonSerializerSettings; - private readonly string _baseSchemeUrlAndPort; - private BearerToken _token; - private readonly IInternalConfiguration _config; - private readonly IIdentityServerConfiguration _identityServerConfiguration; - - public HttpPeer(string hostAndPort, HttpClient httpClient, IBaseUrlFinder finder, IInternalConfiguration config, IIdentityServerConfiguration identityServerConfiguration) - { - _identityServerConfiguration = identityServerConfiguration; - _config = config; - Id = hostAndPort; - _hostAndPort = hostAndPort; - _httpClient = httpClient; - _jsonSerializerSettings = new JsonSerializerSettings() - { - TypeNameHandling = TypeNameHandling.All - }; - _baseSchemeUrlAndPort = finder.Find(); - } - - public string Id { get; } - - public async Task Request(RequestVote requestVote) - { - if (_token == null) - { - await SetToken(); - } - - var json = JsonConvert.SerializeObject(requestVote, _jsonSerializerSettings); - var content = new StringContent(json); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var response = await _httpClient.PostAsync($"{_hostAndPort}/administration/raft/requestvote", content); - if (response.IsSuccessStatusCode) - { - return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync(), _jsonSerializerSettings); - } - - return new RequestVoteResponse(false, requestVote.Term); - } - - public async Task Request(AppendEntries appendEntries) - { - try - { - if (_token == null) - { - await SetToken(); - } - - var json = JsonConvert.SerializeObject(appendEntries, _jsonSerializerSettings); - var content = new StringContent(json); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var response = await _httpClient.PostAsync($"{_hostAndPort}/administration/raft/appendEntries", content); - if (response.IsSuccessStatusCode) - { - return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync(), _jsonSerializerSettings); - } - - return new AppendEntriesResponse(appendEntries.Term, false); - } - catch (Exception ex) - { - Console.WriteLine(ex); - return new AppendEntriesResponse(appendEntries.Term, false); - } - } - - public async Task> Request(T command) - where T : ICommand - { - Console.WriteLine("SENDING REQUEST...."); - if (_token == null) - { - await SetToken(); - } - - var json = JsonConvert.SerializeObject(command, _jsonSerializerSettings); - var content = new StringContent(json); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var response = await _httpClient.PostAsync($"{_hostAndPort}/administration/raft/command", content); - if (response.IsSuccessStatusCode) - { - Console.WriteLine("REQUEST OK...."); - var okResponse = JsonConvert.DeserializeObject>(await response.Content.ReadAsStringAsync(), _jsonSerializerSettings); - return new OkResponse((T)okResponse.Command); - } - - Console.WriteLine("REQUEST NOT OK...."); - return new ErrorResponse(await response.Content.ReadAsStringAsync(), command); - } - - private async Task SetToken() - { - var tokenUrl = $"{_baseSchemeUrlAndPort}{_config.AdministrationPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", _identityServerConfiguration.ApiName), - new KeyValuePair("client_secret", _identityServerConfiguration.ApiSecret), - new KeyValuePair("scope", _identityServerConfiguration.ApiName), - new KeyValuePair("grant_type", "client_credentials") - }; - var content = new FormUrlEncodedContent(formData); - var response = await _httpClient.PostAsync(tokenUrl, content); - var responseContent = await response.Content.ReadAsStringAsync(); - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(_token.TokenType, _token.AccessToken); - } - } -} diff --git a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj b/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj deleted file mode 100644 index 7bb03727d..000000000 --- a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - net5.0 - true - Provides Ocelot extensions to use Rafty - Ocelot.Provider.Rafty - 0.0.0-dev - Ocelot.Provider.Rafty - Ocelot.Provider.Rafty - API Gateway;.NET core - https://github.com/ThreeMammals/Ocelot.Provider.Rafty - https://github.com/ThreeMammals/Ocelot.Provider.Rafty - http://threemammals.com/images/ocelot_logo.png - win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64 - false - false - True - false - Tom Pallister - ..\..\codeanalysis.ruleset - - - full - True - - - - - - - - - - all - - - - - - diff --git a/src/Ocelot.Provider.Rafty/OcelotAdministrationBuilderExtensions.cs b/src/Ocelot.Provider.Rafty/OcelotAdministrationBuilderExtensions.cs deleted file mode 100644 index a04876594..000000000 --- a/src/Ocelot.Provider.Rafty/OcelotAdministrationBuilderExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Configuration.Setter; - using DependencyInjection; - using global::Rafty.Concensus.Node; - using global::Rafty.FiniteStateMachine; - using global::Rafty.Infrastructure; - using global::Rafty.Log; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.DependencyInjection.Extensions; - - public static class OcelotAdministrationBuilderExtensions - { - public static IOcelotAdministrationBuilder AddRafty(this IOcelotAdministrationBuilder builder) - { - var settings = new InMemorySettings(4000, 6000, 100, 10000); - builder.Services.RemoveAll(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(settings); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.Configure(builder.ConfigurationRoot); - return builder; - } - } -} diff --git a/src/Ocelot.Provider.Rafty/OcelotFiniteStateMachine.cs b/src/Ocelot.Provider.Rafty/OcelotFiniteStateMachine.cs deleted file mode 100644 index b054852ee..000000000 --- a/src/Ocelot.Provider.Rafty/OcelotFiniteStateMachine.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Configuration.Setter; - using global::Rafty.FiniteStateMachine; - using global::Rafty.Log; - using System.Threading.Tasks; - - public class OcelotFiniteStateMachine : IFiniteStateMachine - { - private readonly IFileConfigurationSetter _setter; - - public OcelotFiniteStateMachine(IFileConfigurationSetter setter) - { - _setter = setter; - } - - public async Task Handle(LogEntry log) - { - //todo - handle an error - //hack it to just cast as at the moment we know this is the only command :P - var hack = (UpdateFileConfiguration)log.CommandData; - await _setter.Set(hack.Configuration); - } - } -} diff --git a/src/Ocelot.Provider.Rafty/Properties/AssemblyInfo.cs b/src/Ocelot.Provider.Rafty/Properties/AssemblyInfo.cs deleted file mode 100644 index dd8b7610c..000000000 --- a/src/Ocelot.Provider.Rafty/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Ocelot")] -[assembly: AssemblyTrademark("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("d6df4206-0dba-41d8-884d-c3e08290fdbb")] diff --git a/src/Ocelot.Provider.Rafty/RaftController.cs b/src/Ocelot.Provider.Rafty/RaftController.cs deleted file mode 100644 index a8006cdce..000000000 --- a/src/Ocelot.Provider.Rafty/RaftController.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using global::Rafty.Concensus.Messages; - using global::Rafty.Concensus.Node; - using global::Rafty.FiniteStateMachine; - using Logging; - using Microsoft.AspNetCore.Authorization; - using Microsoft.AspNetCore.Mvc; - using Middleware; - using Newtonsoft.Json; - using System; - using System.IO; - using System.Threading.Tasks; - - [Authorize] - [Route("raft")] - public class RaftController : Controller - { - private readonly INode _node; - private readonly IOcelotLogger _logger; - private readonly string _baseSchemeUrlAndPort; - private readonly JsonSerializerSettings _jsonSerialiserSettings; - - public RaftController(INode node, IOcelotLoggerFactory loggerFactory, IBaseUrlFinder finder) - { - _jsonSerialiserSettings = new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }; - _baseSchemeUrlAndPort = finder.Find(); - _logger = loggerFactory.CreateLogger(); - _node = node; - } - - [Route("appendentries")] - public async Task AppendEntries() - { - using (var reader = new StreamReader(HttpContext.Request.Body)) - { - var json = await reader.ReadToEndAsync(); - - var appendEntries = JsonConvert.DeserializeObject(json, _jsonSerialiserSettings); - - _logger.LogDebug($"{_baseSchemeUrlAndPort}/appendentries called, my state is {_node.State.GetType().FullName}"); - - var appendEntriesResponse = await _node.Handle(appendEntries); - - return new OkObjectResult(appendEntriesResponse); - } - } - - [Route("requestvote")] - public async Task RequestVote() - { - using (var reader = new StreamReader(HttpContext.Request.Body)) - { - var json = await reader.ReadToEndAsync(); - - var requestVote = JsonConvert.DeserializeObject(json, _jsonSerialiserSettings); - - _logger.LogDebug($"{_baseSchemeUrlAndPort}/requestvote called, my state is {_node.State.GetType().FullName}"); - - var requestVoteResponse = await _node.Handle(requestVote); - - return new OkObjectResult(requestVoteResponse); - } - } - - [Route("command")] - public async Task Command() - { - try - { - using (var reader = new StreamReader(HttpContext.Request.Body)) - { - var json = await reader.ReadToEndAsync(); - - var command = JsonConvert.DeserializeObject(json, _jsonSerialiserSettings); - - _logger.LogDebug($"{_baseSchemeUrlAndPort}/command called, my state is {_node.State.GetType().FullName}"); - - var commandResponse = await _node.Accept(command); - - json = JsonConvert.SerializeObject(commandResponse, _jsonSerialiserSettings); - - return StatusCode(200, json); - } - } - catch (Exception e) - { - _logger.LogError($"THERE WAS A PROBLEM ON NODE {_node.State.CurrentState.Id}", e); - throw; - } - } - } -} diff --git a/src/Ocelot.Provider.Rafty/RaftyFileConfigurationSetter.cs b/src/Ocelot.Provider.Rafty/RaftyFileConfigurationSetter.cs deleted file mode 100644 index 21fc97a81..000000000 --- a/src/Ocelot.Provider.Rafty/RaftyFileConfigurationSetter.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Configuration.File; - using Configuration.Setter; - using global::Rafty.Concensus.Node; - using global::Rafty.Infrastructure; - using System.Threading.Tasks; - - public class RaftyFileConfigurationSetter : IFileConfigurationSetter - { - private readonly INode _node; - - public RaftyFileConfigurationSetter(INode node) - { - _node = node; - } - - public async Task Set(FileConfiguration fileConfiguration) - { - var result = await _node.Accept(new UpdateFileConfiguration(fileConfiguration)); - - if (result.GetType() == typeof(ErrorResponse)) - { - return new Responses.ErrorResponse(new UnableToSaveAcceptCommand($"unable to save file configuration to state machine")); - } - - return new Responses.OkResponse(); - } - } -} diff --git a/src/Ocelot.Provider.Rafty/RaftyMiddlewareConfigurationProvider.cs b/src/Ocelot.Provider.Rafty/RaftyMiddlewareConfigurationProvider.cs deleted file mode 100644 index 62e9711da..000000000 --- a/src/Ocelot.Provider.Rafty/RaftyMiddlewareConfigurationProvider.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using global::Rafty.Concensus.Node; - using global::Rafty.Infrastructure; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.DependencyInjection; - using Middleware; - using System.Threading.Tasks; - - public static class RaftyMiddlewareConfigurationProvider - { - public static OcelotMiddlewareConfigurationDelegate Get = builder => - { - if (UsingRafty(builder)) - { - SetUpRafty(builder); - } - - return Task.CompletedTask; - }; - - private static bool UsingRafty(IApplicationBuilder builder) - { - var node = builder.ApplicationServices.GetService(); - if (node != null) - { - return true; - } - - return false; - } - - private static void SetUpRafty(IApplicationBuilder builder) - { - var applicationLifetime = builder.ApplicationServices.GetService(); - applicationLifetime.ApplicationStopping.Register(() => OnShutdown(builder)); - var node = builder.ApplicationServices.GetService(); - var nodeId = builder.ApplicationServices.GetService(); - node.Start(nodeId); - } - - private static void OnShutdown(IApplicationBuilder app) - { - var node = app.ApplicationServices.GetService(); - node.Stop(); - } - } -} diff --git a/src/Ocelot.Provider.Rafty/SqlLiteLog.cs b/src/Ocelot.Provider.Rafty/SqlLiteLog.cs deleted file mode 100644 index 1be1645e9..000000000 --- a/src/Ocelot.Provider.Rafty/SqlLiteLog.cs +++ /dev/null @@ -1,334 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using global::Rafty.Infrastructure; - using global::Rafty.Log; - using Microsoft.Data.Sqlite; - using Microsoft.Extensions.Logging; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.IO; - using System.Threading; - using System.Threading.Tasks; - - public class SqlLiteLog : ILog - { - private readonly string _path; - private readonly SemaphoreSlim _sempaphore = new SemaphoreSlim(1, 1); - private readonly ILogger _logger; - private readonly NodeId _nodeId; - - public SqlLiteLog(NodeId nodeId, ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(); - _nodeId = nodeId; - _path = $"{nodeId.Id.Replace("/", "").Replace(":", "")}.db"; - _sempaphore.Wait(); - - if (!File.Exists(_path)) - { - var fs = File.Create(_path); - - fs.Dispose(); - - using (var connection = new SqliteConnection($"Data Source={_path};")) - { - connection.Open(); - - const string sql = @"create table logs ( - id integer primary key, - data text not null - )"; - - 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 async Task LastLogIndex() - { - _sempaphore.Wait(); - 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)) - { - var index = Convert.ToInt32(await command.ExecuteScalarAsync()); - if (index > result) - { - result = index; - } - } - } - - _sempaphore.Release(); - return result; - } - - public async Task LastLogTerm() - { - _sempaphore.Wait(); - 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)) - { - 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) - { - result = log.Term; - } - } - } - - _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 async Task DeleteConflictsFromThisLog(int index, LogEntry logEntry) - { - _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};"; - _logger.LogInformation($"id: {_nodeId.Id} sql: {sql}"); - 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) - { - //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)) - { - var result = await deleteCommand.ExecuteNonQueryAsync(); - } - } - } - } - - _sempaphore.Release(); - } - - public async Task IsDuplicate(int index, LogEntry logEntry) - { - _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); - - if (logEntry != null && log != null && logEntry.Term == log.Term) - { - _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 async Task> GetFrom(int index) - { - _sempaphore.Wait(); - var logsToReturn = new List<(int, LogEntry)>(); - - 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)) - { - using (var reader = await command.ExecuteReaderAsync()) - { - 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)); - } - } - } - - _sempaphore.Release(); - return logsToReturn; - } - } - - public async Task GetTermAtIndex(int index) - { - _sempaphore.Wait(); - 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(await command.ExecuteScalarAsync()); - var jsonSerializerSettings = new JsonSerializerSettings() - { - TypeNameHandling = TypeNameHandling.All - }; - var log = JsonConvert.DeserializeObject(data, jsonSerializerSettings); - if (log != null && log.Term > result) - { - result = log.Term; - } - } - } - - _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/src/Ocelot.Provider.Rafty/UnableToSaveAcceptCommand.cs b/src/Ocelot.Provider.Rafty/UnableToSaveAcceptCommand.cs deleted file mode 100644 index 961cd6ed9..000000000 --- a/src/Ocelot.Provider.Rafty/UnableToSaveAcceptCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Errors; - - public class UnableToSaveAcceptCommand : Error - { - public UnableToSaveAcceptCommand(string message) - : base(message, OcelotErrorCode.UnknownError, 404) - { - } - } -} diff --git a/src/Ocelot.Provider.Rafty/UpdateFileConfiguration.cs b/src/Ocelot.Provider.Rafty/UpdateFileConfiguration.cs deleted file mode 100644 index 894a758a6..000000000 --- a/src/Ocelot.Provider.Rafty/UpdateFileConfiguration.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Ocelot.Provider.Rafty -{ - using Configuration.File; - using global::Rafty.FiniteStateMachine; - - public class UpdateFileConfiguration : ICommand - { - public UpdateFileConfiguration(FileConfiguration configuration) - { - Configuration = configuration; - } - - public FileConfiguration Configuration { get; private set; } - } -} diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index f925df8a1..4cd8640f6 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -25,11 +25,11 @@ - - + + NU1701 - + all diff --git a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs index 08f07f8a3..4c998ef9e 100644 --- a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs +++ b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs @@ -1,11 +1,10 @@ namespace Ocelot.AcceptanceTests { using Butterfly.Client.AspNetCore; - using Configuration.File; + using Ocelot.Configuration.File; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; - using Rafty.Infrastructure; using Shouldly; using System; using System.Collections.Generic; diff --git a/test/Ocelot.AcceptanceTests/ContentTests.cs b/test/Ocelot.AcceptanceTests/ContentTests.cs index 8137bc8a5..1c5aefd8c 100644 --- a/test/Ocelot.AcceptanceTests/ContentTests.cs +++ b/test/Ocelot.AcceptanceTests/ContentTests.cs @@ -147,7 +147,7 @@ private void ThenTheContentTypeIsIs(string expected) private void ThenTheContentLengthShouldBeZero() { - _contentLength.ShouldBeEquivalentTo(0L); + _contentLength.ShouldBeNull(); } private void ThenTheContentLengthIs(int expected) diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 193ce9db5..f3c21df3b 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -39,7 +39,7 @@ - + @@ -50,14 +50,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - + + + + + + + + @@ -66,7 +66,6 @@ - diff --git a/test/Ocelot.AcceptanceTests/OpenTracingTests.cs b/test/Ocelot.AcceptanceTests/OpenTracingTests.cs index d93f9c1b4..8adf54e81 100644 --- a/test/Ocelot.AcceptanceTests/OpenTracingTests.cs +++ b/test/Ocelot.AcceptanceTests/OpenTracingTests.cs @@ -8,7 +8,6 @@ namespace Ocelot.AcceptanceTests using OpenTracing; using OpenTracing.Propagation; using OpenTracing.Tag; - using Rafty.Infrastructure; using Shouldly; using System; using System.Collections.Generic; @@ -17,6 +16,8 @@ namespace Ocelot.AcceptanceTests using TestStack.BDDfy; using Xunit; using Xunit.Abstractions; + using System.Diagnostics; + using System.Threading.Tasks; public class OpenTracingTests : IDisposable { @@ -513,4 +514,70 @@ public IEnumerable> GetBaggageItems() throw new NotImplementedException(); } } + + public class Wait + { + public static Waiter WaitFor(int milliSeconds) + { + return new Waiter(milliSeconds); + } + } + + public class Waiter + { + private readonly int _milliSeconds; + + public Waiter(int milliSeconds) + { + _milliSeconds = milliSeconds; + } + + public bool Until(Func condition) + { + var stopwatch = Stopwatch.StartNew(); + var passed = false; + while (stopwatch.ElapsedMilliseconds < _milliSeconds) + { + if (condition.Invoke()) + { + passed = true; + break; + } + } + + return passed; + } + + public async Task Until(Func> condition) + { + var stopwatch = Stopwatch.StartNew(); + var passed = false; + while (stopwatch.ElapsedMilliseconds < _milliSeconds) + { + if (await condition.Invoke()) + { + passed = true; + break; + } + } + + return passed; + } + + public bool Until(Func condition) + { + var stopwatch = Stopwatch.StartNew(); + var passed = false; + while (stopwatch.ElapsedMilliseconds < _milliSeconds) + { + if (condition.Invoke()) + { + passed = true; + break; + } + } + + return passed; + } + } } diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 0fc66ff36..5ef9e8e5a 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -1,61 +1,60 @@ - - - 0.0.0-dev - net5.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 - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - + + + 0.0.0-dev + net5.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 + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + diff --git a/test/Ocelot.IntegrationTests/RaftTests.cs b/test/Ocelot.IntegrationTests/RaftTests.cs deleted file mode 100644 index 359aa6f5e..000000000 --- a/test/Ocelot.IntegrationTests/RaftTests.cs +++ /dev/null @@ -1,513 +0,0 @@ -namespace Ocelot.IntegrationTests -{ - using Administration; - using Configuration.File; - using DependencyInjection; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Data.Sqlite; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Middleware; - using Newtonsoft.Json; - using Ocelot.Provider.Rafty; - using Rafty.Infrastructure; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Threading; - using System.Threading.Tasks; - using Xunit; - using Xunit.Abstractions; - using Wait = Rafty.Infrastructure.Wait; - - public class RaftTests : IDisposable - { - private readonly List _builders; - private readonly List _webHostBuilders; - private readonly List _threads; - private FilePeers _peers; - private HttpClient _httpClient; - private readonly HttpClient _httpClientForAssertions; - private BearerToken _token; - private HttpResponseMessage _response; - private static readonly object _lock = new object(); - private ITestOutputHelper _output; - - public RaftTests(ITestOutputHelper output) - { - _output = output; - _httpClientForAssertions = new HttpClient(); - _webHostBuilders = new List(); - _builders = new List(); - _threads = new List(); - } - - [Fact(Skip = "Still not stable, more work required in rafty..")] - public async Task should_persist_command_to_five_servers() - { - 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 - { - } - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - Port = 80, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.123.123", - Port = 443, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = new List { "post" }, - UpstreamPathTemplate = "/test" - } - } - }; - - var command = new UpdateFileConfiguration(updatedConfiguration); - GivenThePeersAre(peers); - GivenThereIsAConfiguration(configuration); - GivenFiveServersAreRunning(); - await GivenIHaveAnOcelotToken("/administration"); - await WhenISendACommandIntoTheCluster(command); - Thread.Sleep(5000); - await ThenTheCommandIsReplicatedToAllStateMachines(command); - } - - [Fact(Skip = "Still not stable, more work required in rafty..")] - public async Task should_persist_command_to_five_servers_when_using_administration_api() - { - 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 - { - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - Port = 80, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.123.123", - Port = 443, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = new List { "post" }, - UpstreamPathTemplate = "/test" - } - } - }; - - var command = new UpdateFileConfiguration(updatedConfiguration); - GivenThePeersAre(peers); - GivenThereIsAConfiguration(configuration); - GivenFiveServersAreRunning(); - await GivenIHaveAnOcelotToken("/administration"); - GivenIHaveAddedATokenToMyRequest(); - await WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration); - await ThenTheCommandIsReplicatedToAllStateMachines(command); - } - - private void GivenThePeersAre(List peers) - { - 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 - { - var p = _peers.Peers.First(); - var json = JsonConvert.SerializeObject(command, new JsonSerializerSettings() - { - TypeNameHandling = TypeNameHandling.All - }); - var httpContent = new StringContent(json); - httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - using (var httpClient = new HttpClient()) - { - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - var response = await httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent); - response.EnsureSuccessStatusCode(); - var content = await response.Content.ReadAsStringAsync(); - - var errorResult = JsonConvert.DeserializeObject>(content); - - if (!string.IsNullOrEmpty(errorResult.Error)) - { - return false; - } - - var okResult = JsonConvert.DeserializeObject>(content); - - if (okResult.Command.Configuration.Routes.Count == 2) - { - return true; - } - } - - return false; - } - catch (Exception e) - { - Console.WriteLine(e); - return false; - } - } - - var commandSent = await Wait.WaitFor(40000).Until(async () => - { - var result = await SendCommand(); - Thread.Sleep(1000); - return result; - }); - - commandSent.ShouldBeTrue(); - } - - private async Task ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds) - { - async Task CommandCalledOnAllStateMachines() - { - try - { - var passed = 0; - foreach (var peer in _peers.Peers) - { - var path = $"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"; - 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()); - index.ShouldBe(1); - } - } - - _httpClientForAssertions.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - 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); - response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < response.Routes.Count; i++) - { - for (var j = 0; j < response.Routes[i].DownstreamHostAndPorts.Count; j++) - { - var res = response.Routes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.Configuration.Routes[i].DownstreamHostAndPorts[j]; - res.Host.ShouldBe(expected.Host); - res.Port.ShouldBe(expected.Port); - } - - response.Routes[i].DownstreamPathTemplate.ShouldBe(expecteds.Configuration.Routes[i].DownstreamPathTemplate); - response.Routes[i].DownstreamScheme.ShouldBe(expecteds.Configuration.Routes[i].DownstreamScheme); - response.Routes[i].UpstreamPathTemplate.ShouldBe(expecteds.Configuration.Routes[i].UpstreamPathTemplate); - response.Routes[i].UpstreamHttpMethod.ShouldBe(expecteds.Configuration.Routes[i].UpstreamHttpMethod); - } - - passed++; - } - - return passed == 5; - } - catch (Exception e) - { - //_output.WriteLine($"{e.Message}, {e.StackTrace}"); - Console.WriteLine(e); - return false; - } - } - - var commandOnAllStateMachines = await Wait.WaitFor(40000).Until(async () => - { - var result = await CommandCalledOnAllStateMachines(); - Thread.Sleep(1000); - return result; - }); - - commandOnAllStateMachines.ShouldBeTrue(); - } - - private async Task WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) - { - async Task SendCommand() - { - var json = JsonConvert.SerializeObject(updatedConfiguration); - - var content = new StringContent(json); - - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - - _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.") - { - return false; - } - - if (string.IsNullOrEmpty(responseContent)) - { - return false; - } - - return _response.IsSuccessStatusCode; - } - - var commandSent = await Wait.WaitFor(40000).Until(async () => - { - var result = await SendCommand(); - Thread.Sleep(1000); - return result; - }); - - commandSent.ShouldBeTrue(); - } - - private void GivenIHaveAddedATokenToMyRequest() - { - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - } - - private async Task GivenIHaveAnOcelotToken(string adminPath) - { - async Task AddToken() - { - try - { - var tokenUrl = $"{adminPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "admin"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "admin"), - new KeyValuePair("grant_type", "client_credentials") - }; - var content = new FormUrlEncodedContent(formData); - - var response = await _httpClient.PostAsync(tokenUrl, content); - var responseContent = await response.Content.ReadAsStringAsync(); - if (!response.IsSuccessStatusCode) - { - return false; - } - - _token = JsonConvert.DeserializeObject(responseContent); - var configPath = $"{adminPath}/.well-known/openid-configuration"; - response = await _httpClient.GetAsync(configPath); - return response.IsSuccessStatusCode; - } - catch (Exception) - { - return false; - } - } - - var addToken = await Wait.WaitFor(40000).Until(async () => - { - var result = await AddToken(); - Thread.Sleep(1000); - return result; - }); - - addToken.ShouldBeTrue(); - } - - private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - var text = File.ReadAllText(configurationPath); - - configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - text = File.ReadAllText(configurationPath); - } - - private void GivenAServerIsRunning(string url) - { - lock (_lock) - { - IWebHostBuilder webHostBuilder = new WebHostBuilder(); - webHostBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .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.AddJsonFile("peers.json", optional: true, reloadOnChange: false); -#pragma warning disable CS0618 - config.AddOcelotBaseUrl(url); -#pragma warning restore CS0618 - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddSingleton(new NodeId(url)); - x - .AddOcelot() - .AddAdministration("/administration", "secret") - .AddRafty(); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - - var builder = webHostBuilder.Build(); - builder.Start(); - - _webHostBuilders.Add(webHostBuilder); - _builders.Add(builder); - } - } - - private void GivenFiveServersAreRunning() - { - var bytes = File.ReadAllText("peers.json"); - _peers = JsonConvert.DeserializeObject(bytes); - - foreach (var peer in _peers.Peers) - { - File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", "")); - File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"); - var thread = new Thread(() => GivenAServerIsRunning(peer.HostAndPort)); - thread.Start(); - _threads.Add(thread); - } - } - - public void Dispose() - { - foreach (var builder in _builders) - { - builder?.Dispose(); - } - - foreach (var peer in _peers.Peers) - { - try - { - File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", "")); - File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - } - } -} diff --git a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj index d4e02c673..3eae2086b 100644 --- a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj +++ b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj @@ -28,13 +28,13 @@ - - - - - - - + + + + + + + all diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 439d72f30..a43e88485 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -31,7 +31,6 @@ - @@ -50,7 +49,7 @@ - + all @@ -60,13 +59,13 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + + @@ -80,7 +79,6 @@ - diff --git a/test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs deleted file mode 100644 index 212d92931..000000000 --- a/test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace Ocelot.UnitTests.Rafty -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Administration; - using Ocelot.DependencyInjection; - using Provider.Rafty; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Reflection; - using TestStack.BDDfy; - using Xunit; - - public class OcelotAdministrationBuilderExtensionsTests - { - private readonly IServiceCollection _services; - private IServiceProvider _serviceProvider; - private readonly IConfiguration _configRoot; - private IOcelotBuilder _ocelotBuilder; - private Exception _ex; - - public OcelotAdministrationBuilderExtensionsTests() - { - _configRoot = new ConfigurationRoot(new List()); - _services = new ServiceCollection(); - _services.AddSingleton(GetHostingEnvironment()); - _services.AddSingleton(_configRoot); - } - - private IWebHostEnvironment GetHostingEnvironment() - { - var environment = new Mock(); - environment - .Setup(e => e.ApplicationName) - .Returns(typeof(OcelotAdministrationBuilderExtensionsTests).GetTypeInfo().Assembly.GetName().Name); - - return environment.Object; - } - - [Fact] - public void should_set_up_rafty() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpRafty()) - .Then(x => ThenAnExceptionIsntThrown()) - .Then(x => ThenTheCorrectAdminPathIsRegitered()) - .BDDfy(); - } - - private void WhenISetUpRafty() - { - try - { - _ocelotBuilder.AddAdministration("/administration", "secret").AddRafty(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenISetUpOcelotServices() - { - try - { - _ocelotBuilder = _services.AddOcelot(_configRoot); - } - catch (Exception e) - { - _ex = e; - } - } - - private void ThenAnExceptionIsntThrown() - { - _ex.ShouldBeNull(); - } - - private void ThenTheCorrectAdminPathIsRegitered() - { - _serviceProvider = _services.BuildServiceProvider(); - var path = _serviceProvider.GetService(); - path.Path.ShouldBe("/administration"); - } - } -} diff --git a/test/Ocelot.UnitTests/Rafty/OcelotFiniteStateMachineTests.cs b/test/Ocelot.UnitTests/Rafty/OcelotFiniteStateMachineTests.cs deleted file mode 100644 index 53dc3476c..000000000 --- a/test/Ocelot.UnitTests/Rafty/OcelotFiniteStateMachineTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Ocelot.UnitTests.Rafty -{ - using Moq; - using Ocelot.Configuration.Setter; - using Provider.Rafty; - using TestStack.BDDfy; - using Xunit; - - public class OcelotFiniteStateMachineTests - { - private UpdateFileConfiguration _command; - private readonly OcelotFiniteStateMachine _fsm; - private readonly Mock _setter; - - public OcelotFiniteStateMachineTests() - { - _setter = new Mock(); - _fsm = new OcelotFiniteStateMachine(_setter.Object); - } - - [Fact] - public void should_handle_update_file_configuration_command() - { - this.Given(x => GivenACommand(new UpdateFileConfiguration(new Ocelot.Configuration.File.FileConfiguration()))) - .When(x => WhenTheCommandIsHandled()) - .Then(x => ThenTheStateIsUpdated()) - .BDDfy(); - } - - private void GivenACommand(UpdateFileConfiguration command) - { - _command = command; - } - - private void WhenTheCommandIsHandled() - { - _fsm.Handle(new global::Rafty.Log.LogEntry(_command, _command.GetType(), 0)).Wait(); - } - - private void ThenTheStateIsUpdated() - { - _setter.Verify(x => x.Set(_command.Configuration), Times.Once); - } - } -} diff --git a/test/Ocelot.UnitTests/Rafty/RaftyFileConfigurationSetterTests.cs b/test/Ocelot.UnitTests/Rafty/RaftyFileConfigurationSetterTests.cs deleted file mode 100644 index fac7d2fa9..000000000 --- a/test/Ocelot.UnitTests/Rafty/RaftyFileConfigurationSetterTests.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace Ocelot.UnitTests.Rafty -{ - using global::Rafty.Concensus.Node; - using global::Rafty.Infrastructure; - using Moq; - using Ocelot.Configuration.File; - using Provider.Rafty; - using Shouldly; - using System.Threading.Tasks; - using Xunit; - - public class RaftyFileConfigurationSetterTests - { - private readonly RaftyFileConfigurationSetter _setter; - private readonly Mock _node; - - public RaftyFileConfigurationSetterTests() - { - _node = new Mock(); - _setter = new RaftyFileConfigurationSetter(_node.Object); - } - - [Fact] - public async Task should_return_ok() - { - var fileConfig = new FileConfiguration(); - - var response = new OkResponse(new UpdateFileConfiguration(fileConfig)); - - _node.Setup(x => x.Accept(It.IsAny())) - .ReturnsAsync(response); - - var result = await _setter.Set(fileConfig); - result.IsError.ShouldBeFalse(); - } - - [Fact] - public async Task should_return_not_ok() - { - var fileConfig = new FileConfiguration(); - - var response = new ErrorResponse("error", new UpdateFileConfiguration(fileConfig)); - - _node.Setup(x => x.Accept(It.IsAny())) - .ReturnsAsync(response); - - var result = await _setter.Set(fileConfig); - - result.IsError.ShouldBeTrue(); - } - } -} From 6eed692c29a306e0725e1f8b3ec5c06925e13fef Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 27 Nov 2020 16:31:34 +0000 Subject: [PATCH 26/34] more package updates --- src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj | 2 +- test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj | 2 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj index a9cceebe4..8764b0122 100644 --- a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj +++ b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj @@ -30,7 +30,7 @@ all - + diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index f3c21df3b..c3b726a5c 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -41,7 +41,7 @@ - + all diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index a43e88485..22888f7ba 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -66,7 +66,7 @@ - + @@ -78,7 +78,7 @@ - + From 32551624bba2a525ad9b3fd82ae6c0521654855a Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 27 Nov 2020 19:01:27 +0000 Subject: [PATCH 27/34] wip tests failing --- src/Ocelot.Provider.Eureka/Eureka.cs | 70 +-- .../EurekaProviderFactory.cs | 6 +- .../Ocelot.Provider.Eureka.csproj | 2 +- .../ClaimsToDownstreamPathTests.cs | 7 +- .../ClaimsToQueryStringForwardingTests.cs | 481 +++++++------- .../HttpClientCachingTests.cs | 9 +- .../Ocelot.AcceptanceTests.csproj | 6 +- .../ServiceDiscoveryTests.cs | 7 +- test/Ocelot.AcceptanceTests/WebSocketTests.cs | 8 +- .../Ocelot.IntegrationTests.csproj | 4 +- .../ConsulServiceDiscoveryProviderTests.cs | 595 +++++++++--------- ...ekaMiddlewareConfigurationProviderTests.cs | 6 +- .../Eureka/EurekaProviderFactoryTests.cs | 2 +- .../EurekaServiceDiscoveryProviderTests.cs | 233 +++---- .../Headers/AddHeadersToRequestPlainTests.cs | 255 ++++---- .../Infrastructure/HttpDataRepositoryTests.cs | 174 ++--- .../KubeServiceDiscoveryProviderTests.cs | 297 ++++----- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 6 +- .../Request/Mapper/RequestMapperTests.cs | 2 +- 19 files changed, 1103 insertions(+), 1067 deletions(-) diff --git a/src/Ocelot.Provider.Eureka/Eureka.cs b/src/Ocelot.Provider.Eureka/Eureka.cs index 8a064549e..9da8decd1 100644 --- a/src/Ocelot.Provider.Eureka/Eureka.cs +++ b/src/Ocelot.Provider.Eureka/Eureka.cs @@ -1,35 +1,35 @@ -namespace Ocelot.Provider.Eureka -{ - using ServiceDiscovery.Providers; - using Steeltoe.Common.Discovery; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Values; - - public class Eureka : IServiceDiscoveryProvider - { - private readonly IDiscoveryClient _client; - private readonly string _serviceName; - - public Eureka(string serviceName, IDiscoveryClient client) - { - _client = client; - _serviceName = serviceName; - } - - public Task> Get() - { - var services = new List(); - - var instances = _client.GetInstances(_serviceName); - - if (instances != null && instances.Any()) - { - services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List()))); - } - - return Task.FromResult(services); - } - } -} +namespace Ocelot.Provider.Eureka +{ + using Ocelot.ServiceDiscovery.Providers; + using Steeltoe.Discovery; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Ocelot.Values; + + public class Eureka : IServiceDiscoveryProvider + { + private readonly IDiscoveryClient _client; + private readonly string _serviceName; + + public Eureka(string serviceName, IDiscoveryClient client) + { + _client = client; + _serviceName = serviceName; + } + + public Task> Get() + { + var services = new List(); + + var instances = _client.GetInstances(_serviceName); + + if (instances != null && instances.Any()) + { + services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List()))); + } + + return Task.FromResult(services); + } + } +} diff --git a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs index 550a83a3b..d347f0af8 100644 --- a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs +++ b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs @@ -1,9 +1,9 @@ namespace Ocelot.Provider.Eureka { using Microsoft.Extensions.DependencyInjection; - using ServiceDiscovery; - using Steeltoe.Common.Discovery; - + using Ocelot.ServiceDiscovery; + using Steeltoe.Discovery; + public static class EurekaProviderFactory { public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) => diff --git a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj index 8fa630305..ea7275040 100644 --- a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj +++ b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj @@ -27,7 +27,7 @@ - + all diff --git a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs index 647948ec3..47238e88a 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs @@ -96,8 +96,13 @@ public void should_return_200_and_change_downstream_path() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/users")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231")) - .And(x => _downstreamFinalPath.ShouldBe("/users/1231231")) + .And(x => ThenTheDownstreamPathIs("/users/1231231")) .BDDfy(); + } + + private void ThenTheDownstreamPathIs(string path) + { + _downstreamFinalPath.ShouldBe(path); } private void GivenThereIsAServiceRunningOn(string url, int statusCode) diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index cfdbdbb49..978a54611 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -1,60 +1,60 @@ -using IdentityServer4.AccessTokenValidation; -using IdentityServer4.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Primitives; -using Ocelot.Configuration.File; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Security.Claims; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.AcceptanceTests -{ - using IdentityServer4.Test; - using Shouldly; - - public class ClaimsToQueryStringForwardingTests : IDisposable - { - private IWebHost _servicebuilder; - private IWebHost _identityServerBuilder; - private readonly Steps _steps; - private Action _options; - private string _identityServerRootUrl; - private string _downstreamQueryString; - - public ClaimsToQueryStringForwardingTests() - { +using IdentityServer4.AccessTokenValidation; +using IdentityServer4.Models; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Primitives; +using Ocelot.Configuration.File; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Security.Claims; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + using IdentityServer4.Test; + using Shouldly; + + public class ClaimsToQueryStringForwardingTests : IDisposable + { + private IWebHost _servicebuilder; + private IWebHost _identityServerBuilder; + private readonly Steps _steps; + private Action _options; + private string _identityServerRootUrl; + private string _downstreamQueryString; + + public ClaimsToQueryStringForwardingTests() + { _steps = new Steps(); var identityServerPort = RandomPortFinder.GetRandomPort(); - _identityServerRootUrl = $"http://localhost:{identityServerPort}"; - _options = o => - { - o.Authority = _identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - } - - [Fact] - public void should_return_response_200_and_foward_claim_as_query_string() + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; + _options = o => + { + o.Authority = _identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + } + + [Fact] + public void should_return_response_200_and_foward_claim_as_query_string() { var user = new TestUser() { Username = "test", Password = "test", SubjectId = "registered|1231231", - Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "1") + Claims = new List + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "1") } }; @@ -62,38 +62,38 @@ public void should_return_response_200_and_foward_claim_as_query_string() var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List - { - "openid", "offline_access", "api" - }, - }, - AddQueriesToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api" + }, + }, + AddQueriesToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"} + } + } } }; @@ -106,21 +106,21 @@ public void should_return_response_200_and_foward_claim_as_query_string() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_and_foward_claim_as_query_string_and_preserve_original_string() + .BDDfy(); + } + + [Fact] + public void should_return_response_200_and_foward_claim_as_query_string_and_preserve_original_string() { var user = new TestUser() { Username = "test", Password = "test", SubjectId = "registered|1231231", - Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "1") + Claims = new List + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "1") } }; @@ -128,38 +128,38 @@ public void should_return_response_200_and_foward_claim_as_query_string_and_pres var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List - { - "openid", "offline_access", "api" - }, - }, - AddQueriesToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api" + }, + }, + AddQueriesToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"} + } + } } }; @@ -172,120 +172,125 @@ public void should_return_response_200_and_foward_claim_as_query_string_and_pres .When(x => _steps.WhenIGetUrlOnTheApiGateway("/?test=1&test=2")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) - .And(_ => _downstreamQueryString.ShouldBe("?test=1&test=2&CustomerId=123&LocationId=1&UserId=1231231&UserType=registered")) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string url, int statusCode) - { - _servicebuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - _downstreamQueryString = context.Request.QueryString.Value; - - StringValues customerId; - context.Request.Query.TryGetValue("CustomerId", out customerId); - - StringValues locationId; - context.Request.Query.TryGetValue("LocationId", out locationId); - - StringValues userType; - context.Request.Query.TryGetValue("UserType", out userType); - - StringValues userId; - context.Request.Query.TryGetValue("UserId", out userId); - - var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}"; - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }) - .Build(); - - _servicebuilder.Start(); - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) - { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = "My API", - Enabled = true, - DisplayName = "test", - Scopes = new List() - { - new Scope("api"), - new Scope("openid"), - new Scope("offline_access") - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId", "UserType", "UserId" - } - } - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = "client", - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "openid", "offline_access" }, - AccessTokenType = tokenType, - Enabled = true, - RequireClientSecret = false - } - }) - .AddTestUsers(new List - { - user - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); - - _identityServerBuilder.Start(); - - _steps.VerifyIdentiryServerStarted(url); - } - - public void Dispose() - { - _servicebuilder?.Dispose(); - _steps.Dispose(); - _identityServerBuilder?.Dispose(); - } - } + .And(_ => ThenTheQueryStringIs("?test=1&test=2&CustomerId=123&LocationId=1&UserId=1231231&UserType=registered")) + .BDDfy(); + } + + private void ThenTheQueryStringIs(string queryString) + { + _downstreamQueryString.ShouldBe(queryString); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode) + { + _servicebuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + _downstreamQueryString = context.Request.QueryString.Value; + + StringValues customerId; + context.Request.Query.TryGetValue("CustomerId", out customerId); + + StringValues locationId; + context.Request.Query.TryGetValue("LocationId", out locationId); + + StringValues userType; + context.Request.Query.TryGetValue("UserType", out userType); + + StringValues userId; + context.Request.Query.TryGetValue("UserId", out userId); + + var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}"; + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _servicebuilder.Start(); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + new Scope("api"), + new Scope("openid"), + new Scope("offline_access") + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256() + } + }, + UserClaims = new List() + { + "CustomerId", "LocationId", "UserType", "UserId" + } + } + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false + } + }) + .AddTestUsers(new List + { + user + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + public void Dispose() + { + _servicebuilder?.Dispose(); + _steps.Dispose(); + _identityServerBuilder?.Dispose(); + } + } } diff --git a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs index 129935d48..ab9f7dffe 100644 --- a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs @@ -62,7 +62,7 @@ public void should_cache_one_http_client_same_re_route() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => cache.Count.ShouldBe(1)) + .And(x => ThenTheCountShouldBe(cache, 1)) .BDDfy(); } @@ -122,10 +122,15 @@ public void should_cache_two_http_client_different_re_route() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => cache.Count.ShouldBe(2)) + .And(x => ThenTheCountShouldBe(cache, 2)) .BDDfy(); } + private void ThenTheCountShouldBe(FakeHttpClientCache cache, int count) + { + cache.Count.ShouldBe(count); + } + private void GivenThereIsAServiceRunningOn(string baseUrl, int statusCode, string responseBody) { _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context => diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index c3b726a5c..850f8276b 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -46,7 +46,7 @@ all - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -59,7 +59,7 @@ - + @@ -68,7 +68,7 @@ - + diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs index 329c410ed..f56e3d281 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -322,7 +322,7 @@ public void should_use_token_to_make_request_to_consul() .When(_ => _steps.WhenIGetUrlOnTheApiGateway("/home")) .Then(_ => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(_ => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(_ => _receivedToken.ShouldBe(token)) + .And(_ => ThenTheTokenIs(token)) .BDDfy(); } @@ -462,6 +462,11 @@ public void should_handle_request_to_poll_consul_for_downstream_service_and_make .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); + } + + private void ThenTheTokenIs(string token) + { + _receivedToken.ShouldBe(token); } private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo) diff --git a/test/Ocelot.AcceptanceTests/WebSocketTests.cs b/test/Ocelot.AcceptanceTests/WebSocketTests.cs index 048484b6a..80e9b5f09 100644 --- a/test/Ocelot.AcceptanceTests/WebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/WebSocketTests.cs @@ -57,7 +57,7 @@ public void should_proxy_websocket_input_to_downstream_service() .And(_ => _steps.StartFakeOcelotWithWebSockets()) .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) .When(_ => StartClient("ws://localhost:5000/")) - .Then(_ => _firstRecieved.Count.ShouldBe(10)) + .Then(_ => ThenTheReceivedCountIs(10)) .BDDfy(); } @@ -323,8 +323,12 @@ private async Task Message(WebSocket webSocket) { Console.WriteLine(e); } + } + + private void ThenTheReceivedCountIs(int count) + { + _firstRecieved.Count.ShouldBe(count); } - public void Dispose() { _serviceHandler?.Dispose(); diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 5ef9e8e5a..7d862b24f 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -35,7 +35,7 @@ all - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -48,7 +48,7 @@ - + diff --git a/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs b/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs index 8e06f1115..8c385e2bd 100644 --- a/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs +++ b/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs @@ -1,299 +1,304 @@ -namespace Ocelot.UnitTests.Consul -{ - using global::Consul; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Moq; - using Newtonsoft.Json; - using Ocelot.Logging; - using Provider.Consul; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using TestStack.BDDfy; - using Values; - using Xunit; - - public class ConsulServiceDiscoveryProviderTests : IDisposable - { - private IWebHost _fakeConsulBuilder; - private readonly List _serviceEntries; - private Consul _provider; - private readonly string _serviceName; - private readonly int _port; - private readonly string _consulHost; - private readonly string _consulScheme; - private readonly string _fakeConsulServiceDiscoveryUrl; - private List _services; - private readonly Mock _factory; - private readonly Mock _logger; - private string _receivedToken; - private readonly IConsulClientFactory _clientFactory; - - public ConsulServiceDiscoveryProviderTests() - { - _serviceName = "test"; - _port = 8500; - _consulHost = "localhost"; - _consulScheme = "http"; - _fakeConsulServiceDiscoveryUrl = $"{_consulScheme}://{_consulHost}:{_port}"; - _serviceEntries = new List(); - _factory = new Mock(); - _clientFactory = new ConsulClientFactory(); - _logger = new Mock(); - _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, null); - _provider = new Consul(config, _factory.Object, _clientFactory); - } - - [Fact] - public void should_return_service_from_consul() - { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(1)) - .BDDfy(); +namespace Ocelot.UnitTests.Consul +{ + using global::Consul; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Moq; + using Newtonsoft.Json; + using Ocelot.Logging; + using Provider.Consul; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using TestStack.BDDfy; + using Values; + using Xunit; + + public class ConsulServiceDiscoveryProviderTests : IDisposable + { + private IWebHost _fakeConsulBuilder; + private readonly List _serviceEntries; + private Consul _provider; + private readonly string _serviceName; + private readonly int _port; + private readonly string _consulHost; + private readonly string _consulScheme; + private readonly string _fakeConsulServiceDiscoveryUrl; + private List _services; + private readonly Mock _factory; + private readonly Mock _logger; + private string _receivedToken; + private readonly IConsulClientFactory _clientFactory; + + public ConsulServiceDiscoveryProviderTests() + { + _serviceName = "test"; + _port = 8500; + _consulHost = "localhost"; + _consulScheme = "http"; + _fakeConsulServiceDiscoveryUrl = $"{_consulScheme}://{_consulHost}:{_port}"; + _serviceEntries = new List(); + _factory = new Mock(); + _clientFactory = new ConsulClientFactory(); + _logger = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, null); + _provider = new Consul(config, _factory.Object, _clientFactory); + } + + [Fact] + public void should_return_service_from_consul() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(1)) + .BDDfy(); + } + + [Fact] + public void should_use_token() + { + var token = "test token"; + var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, token); + _provider = new Consul(config, _factory.Object, _clientFactory); + + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0], + }, + }; + + this.Given(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .When(_ => WhenIGetTheServices()) + .Then(_ => ThenTheCountIs(1)) + .And(_ => ThenTheTokenIs(token)) + .BDDfy(); } - - [Fact] - public void should_use_token() - { - var token = "test token"; - var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, token); - _provider = new Consul(config, _factory.Object, _clientFactory); - - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .When(_ => WhenIGetTheServices()) - .Then(_ => ThenTheCountIs(1)) - .And(_ => _receivedToken.ShouldBe(token)) - .BDDfy(); + + [Fact] + public void should_not_return_services_with_invalid_address() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "http://localhost", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "http://localhost", + Port = 50888, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(0)) + .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()) + .BDDfy(); + } + + [Fact] + public void should_not_return_services_with_empty_address() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = null, + Port = 50888, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(0)) + .And(x => ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress()) + .BDDfy(); + } + + [Fact] + public void should_not_return_services_with_invalid_port() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = -1, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = 0, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(0)) + .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()) + .BDDfy(); + } + + private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress() + { + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + } + + private void ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress() + { + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + } + + private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts() + { + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + } + + private void ThenTheCountIs(int count) + { + _services.Count.ShouldBe(count); + } + + private void WhenIGetTheServices() + { + _services = _provider.Get().GetAwaiter().GetResult(); } - [Fact] - public void should_not_return_services_with_invalid_address() + private void ThenTheTokenIs(string token) { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "http://localhost", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "http://localhost", - Port = 50888, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(0)) - .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()) - .BDDfy(); - } - - [Fact] - public void should_not_return_services_with_empty_address() - { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = null, - Port = 50888, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(0)) - .And(x => ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress()) - .BDDfy(); - } - - [Fact] - public void should_not_return_services_with_invalid_port() - { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = -1, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = 0, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(0)) - .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()) - .BDDfy(); - } - - private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress() - { - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - } - - private void ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress() - { - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - } - - private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts() - { - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - } - - private void ThenTheCountIs(int count) - { - _services.Count.ShouldBe(count); - } - - private void WhenIGetTheServices() - { - _services = _provider.Get().GetAwaiter().GetResult(); - } - - private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) - { - foreach (var serviceEntry in serviceEntries) - { - _serviceEntries.Add(serviceEntry); - } - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) - { - _fakeConsulBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") - { - if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) - { - _receivedToken = values.First(); - } - - var json = JsonConvert.SerializeObject(_serviceEntries); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - }) - .Build(); - - _fakeConsulBuilder.Start(); - } - - public void Dispose() - { - _fakeConsulBuilder?.Dispose(); - } - } -} + _receivedToken.ShouldBe(token); + } + + private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) + { + foreach (var serviceEntry in serviceEntries) + { + _serviceEntries.Add(serviceEntry); + } + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + { + _fakeConsulBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + { + if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) + { + _receivedToken = values.First(); + } + + var json = JsonConvert.SerializeObject(_serviceEntries); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + }) + .Build(); + + _fakeConsulBuilder.Start(); + } + + public void Dispose() + { + _fakeConsulBuilder?.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs index 1837a8927..01e2ee30e 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs @@ -6,10 +6,10 @@ using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Repository; - using Provider.Eureka; - using Responses; + using Ocelot.Provider.Eureka; + using Ocelot.Responses; using Shouldly; - using Steeltoe.Common.Discovery; + using Steeltoe.Discovery; using System.Threading.Tasks; using Xunit; diff --git a/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs index 9e1fd3e2c..157359403 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs @@ -5,7 +5,7 @@ using Ocelot.Configuration.Builder; using Provider.Eureka; using Shouldly; - using Steeltoe.Common.Discovery; + using Steeltoe.Discovery; using Xunit; public class EurekaProviderFactoryTests diff --git a/test/Ocelot.UnitTests/Eureka/EurekaServiceDiscoveryProviderTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaServiceDiscoveryProviderTests.cs index ec332605e..2d473ce6f 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaServiceDiscoveryProviderTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaServiceDiscoveryProviderTests.cs @@ -1,117 +1,118 @@ -namespace Ocelot.UnitTests.Eureka -{ - using Moq; - using Provider.Eureka; - using Shouldly; +namespace Ocelot.UnitTests.Eureka +{ + using Moq; + using Ocelot.Provider.Eureka; + using Shouldly; using Steeltoe.Common.Discovery; - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Values; - using Xunit; - - public class EurekaServiceDiscoveryProviderTests - { - private readonly Eureka _provider; - private readonly Mock _client; - private readonly string _serviceId; - private List _instances; - private List _result; - - public EurekaServiceDiscoveryProviderTests() - { - _serviceId = "Laura"; - _client = new Mock(); - _provider = new Eureka(_serviceId, _client.Object); - } - - [Fact] - public void should_return_empty_services() - { - this.When(_ => WhenIGet()) - .Then(_ => ThenTheCountIs(0)) - .BDDfy(); - } - - [Fact] - public void should_return_service_from_client() - { - var instances = new List - { - new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary()) - }; - - this.Given(_ => GivenThe(instances)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheCountIs(1)) - .And(_ => ThenTheClientIsCalledCorrectly()) - .And(_ => ThenTheServiceIsMapped()) - .BDDfy(); - } - - [Fact] - public void should_return_services_from_client() - { - var instances = new List - { - new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary()), - new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary()) - }; - - this.Given(_ => GivenThe(instances)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheCountIs(2)) - .And(_ => ThenTheClientIsCalledCorrectly()) - .BDDfy(); - } - - private void ThenTheServiceIsMapped() - { - _result[0].HostAndPort.DownstreamHost.ShouldBe("somehost"); - _result[0].HostAndPort.DownstreamPort.ShouldBe(801); - _result[0].Name.ShouldBe(_serviceId); - } - - private void ThenTheCountIs(int expected) - { - _result.Count.ShouldBe(expected); - } - - private void ThenTheClientIsCalledCorrectly() - { - _client.Verify(x => x.GetInstances(_serviceId), Times.Once); - } - - private async Task WhenIGet() - { - _result = await _provider.Get(); - } - - private void GivenThe(List instances) - { - _instances = instances; - _client.Setup(x => x.GetInstances(It.IsAny())).Returns(instances); - } - } - - public class EurekaService : IServiceInstance - { - public EurekaService(string serviceId, string host, int port, bool isSecure, Uri uri, IDictionary metadata) - { - ServiceId = serviceId; - Host = host; - Port = port; - IsSecure = isSecure; - Uri = uri; - Metadata = metadata; - } - - public string ServiceId { get; } - public string Host { get; } - public int Port { get; } - public bool IsSecure { get; } - public Uri Uri { get; } - public IDictionary Metadata { get; } - } -} + using Steeltoe.Discovery; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Ocelot.Values; + using Xunit; + + public class EurekaServiceDiscoveryProviderTests + { + private readonly Eureka _provider; + private readonly Mock _client; + private readonly string _serviceId; + private List _instances; + private List _result; + + public EurekaServiceDiscoveryProviderTests() + { + _serviceId = "Laura"; + _client = new Mock(); + _provider = new Eureka(_serviceId, _client.Object); + } + + [Fact] + public void should_return_empty_services() + { + this.When(_ => WhenIGet()) + .Then(_ => ThenTheCountIs(0)) + .BDDfy(); + } + + [Fact] + public void should_return_service_from_client() + { + var instances = new List + { + new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary()) + }; + + this.Given(_ => GivenThe(instances)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheCountIs(1)) + .And(_ => ThenTheClientIsCalledCorrectly()) + .And(_ => ThenTheServiceIsMapped()) + .BDDfy(); + } + + [Fact] + public void should_return_services_from_client() + { + var instances = new List + { + new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary()), + new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary()) + }; + + this.Given(_ => GivenThe(instances)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheCountIs(2)) + .And(_ => ThenTheClientIsCalledCorrectly()) + .BDDfy(); + } + + private void ThenTheServiceIsMapped() + { + _result[0].HostAndPort.DownstreamHost.ShouldBe("somehost"); + _result[0].HostAndPort.DownstreamPort.ShouldBe(801); + _result[0].Name.ShouldBe(_serviceId); + } + + private void ThenTheCountIs(int expected) + { + _result.Count.ShouldBe(expected); + } + + private void ThenTheClientIsCalledCorrectly() + { + _client.Verify(x => x.GetInstances(_serviceId), Times.Once); + } + + private async Task WhenIGet() + { + _result = await _provider.Get(); + } + + private void GivenThe(List instances) + { + _instances = instances; + _client.Setup(x => x.GetInstances(It.IsAny())).Returns(instances); + } + } + + public class EurekaService : IServiceInstance + { + public EurekaService(string serviceId, string host, int port, bool isSecure, Uri uri, IDictionary metadata) + { + ServiceId = serviceId; + Host = host; + Port = port; + IsSecure = isSecure; + Uri = uri; + Metadata = metadata; + } + + public string ServiceId { get; } + public string Host { get; } + public int Port { get; } + public bool IsSecure { get; } + public Uri Uri { get; } + public IDictionary Metadata { get; } + } +} diff --git a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs index cc2916a2d..db2d17b68 100644 --- a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs +++ b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs @@ -1,128 +1,129 @@ -namespace Ocelot.UnitTests.Headers -{ +namespace Ocelot.UnitTests.Headers +{ using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Configuration.Creator; - using Ocelot.Headers; - using Ocelot.Infrastructure; - using Ocelot.Infrastructure.Claims.Parser; - using Ocelot.Logging; - using Responder; - using Responses; - using Shouldly; - using TestStack.BDDfy; - using Xunit; - - public class AddHeadersToRequestPlainTests - { - private readonly AddHeadersToRequest _addHeadersToRequest; - private HttpContext _context; - private AddHeader _addedHeader; - private readonly Mock _placeholders; - private Mock _factory; - private readonly Mock _logger; - - public AddHeadersToRequestPlainTests() - { - _placeholders = new Mock(); - _factory = new Mock(); - _logger = new Mock(); - _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _addHeadersToRequest = new AddHeadersToRequest(Mock.Of(), _placeholders.Object, _factory.Object); - } - - [Fact] - public void should_log_error_if_cannot_find_placeholder() - { - _placeholders.Setup(x => x.Get(It.IsAny())).Returns(new ErrorResponse(new AnyError())); - - this.Given(_ => GivenHttpRequestWithoutHeaders()) - .When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}")) - .Then(_ => ThenAnErrorIsLogged("X-Forwarded-For", "{RemoteIdAddress}")) - .BDDfy(); - } - - [Fact] - public void should_add_placeholder_to_downstream_request() - { - _placeholders.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse("replaced")); - - this.Given(_ => GivenHttpRequestWithoutHeaders()) - .When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}")) - .Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders("replaced")) - .BDDfy(); - } - - [Fact] - public void should_add_plain_text_header_to_downstream_request() - { - this.Given(_ => GivenHttpRequestWithoutHeaders()) - .When(_ => WhenAddingHeader("X-Custom-Header", "PlainValue")) - .Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders()) - .BDDfy(); - } - - [Fact] - public void should_overwrite_existing_header_with_added_header() - { - this.Given(_ => GivenHttpRequestWithHeader("X-Custom-Header", "This should get overwritten")) - .When(_ => WhenAddingHeader("X-Custom-Header", "PlainValue")) - .Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders()) - .BDDfy(); - } - - private void ThenAnErrorIsLogged(string key, string value) - { - _logger.Verify(x => x.LogWarning($"Unable to add header to response {key}: {value}"), Times.Once); - } - - private void GivenHttpRequestWithoutHeaders() - { - _context = new DefaultHttpContext - { - Request = - { - Headers = - { - } - } - }; - } - - private void GivenHttpRequestWithHeader(string headerKey, string headerValue) - { - _context = new DefaultHttpContext - { - Request = - { - Headers = - { - { headerKey, headerValue } - } - } - }; - } - - private void WhenAddingHeader(string headerKey, string headerValue) - { - _addedHeader = new AddHeader(headerKey, headerValue); - _addHeadersToRequest.SetHeadersOnDownstreamRequest(new[] { _addedHeader }, _context); - } - - private void ThenTheHeaderGetsTakenOverToTheRequestHeaders() - { - var requestHeaders = _context.Request.Headers; - requestHeaders.ContainsKey(_addedHeader.Key).ShouldBeTrue($"Header {_addedHeader.Key} was expected but not there."); - var value = requestHeaders[_addedHeader.Key]; - value.ShouldNotBeNull($"Value of header {_addedHeader.Key} was expected to not be null."); - value.ToString().ShouldBe(_addedHeader.Value); - } - - private void ThenTheHeaderGetsTakenOverToTheRequestHeaders(string expected) - { - var requestHeaders = _context.Request.Headers; - var value = requestHeaders[_addedHeader.Key]; - value.ToString().ShouldBe(expected); - } - } -} + using Microsoft.Extensions.Primitives; + using Moq; + using Ocelot.Configuration.Creator; + using Ocelot.Headers; + using Ocelot.Infrastructure; + using Ocelot.Infrastructure.Claims.Parser; + using Ocelot.Logging; + using Responder; + using Responses; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class AddHeadersToRequestPlainTests + { + private readonly AddHeadersToRequest _addHeadersToRequest; + private HttpContext _context; + private AddHeader _addedHeader; + private readonly Mock _placeholders; + private Mock _factory; + private readonly Mock _logger; + + public AddHeadersToRequestPlainTests() + { + _placeholders = new Mock(); + _factory = new Mock(); + _logger = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _addHeadersToRequest = new AddHeadersToRequest(Mock.Of(), _placeholders.Object, _factory.Object); + } + + [Fact] + public void should_log_error_if_cannot_find_placeholder() + { + _placeholders.Setup(x => x.Get(It.IsAny())).Returns(new ErrorResponse(new AnyError())); + + this.Given(_ => GivenHttpRequestWithoutHeaders()) + .When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}")) + .Then(_ => ThenAnErrorIsLogged("X-Forwarded-For", "{RemoteIdAddress}")) + .BDDfy(); + } + + [Fact] + public void should_add_placeholder_to_downstream_request() + { + _placeholders.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse("replaced")); + + this.Given(_ => GivenHttpRequestWithoutHeaders()) + .When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}")) + .Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders("replaced")) + .BDDfy(); + } + + [Fact] + public void should_add_plain_text_header_to_downstream_request() + { + this.Given(_ => GivenHttpRequestWithoutHeaders()) + .When(_ => WhenAddingHeader("X-Custom-Header", "PlainValue")) + .Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders()) + .BDDfy(); + } + + [Fact] + public void should_overwrite_existing_header_with_added_header() + { + this.Given(_ => GivenHttpRequestWithHeader("X-Custom-Header", "This should get overwritten")) + .When(_ => WhenAddingHeader("X-Custom-Header", "PlainValue")) + .Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders()) + .BDDfy(); + } + + private void ThenAnErrorIsLogged(string key, string value) + { + _logger.Verify(x => x.LogWarning($"Unable to add header to response {key}: {value}"), Times.Once); + } + + private void GivenHttpRequestWithoutHeaders() + { + _context = new DefaultHttpContext + { + Request = + { + Headers = + { + } + } + }; + } + + private void GivenHttpRequestWithHeader(string headerKey, string headerValue) + { + _context = new DefaultHttpContext + { + Request = + { + Headers = + { + { headerKey, headerValue } + } + } + }; + } + + private void WhenAddingHeader(string headerKey, string headerValue) + { + _addedHeader = new AddHeader(headerKey, headerValue); + _addHeadersToRequest.SetHeadersOnDownstreamRequest(new[] { _addedHeader }, _context); + } + + private void ThenTheHeaderGetsTakenOverToTheRequestHeaders() + { + var requestHeaders = _context.Request.Headers; + requestHeaders.ContainsKey(_addedHeader.Key).ShouldBeTrue($"Header {_addedHeader.Key} was expected but not there."); + var value = requestHeaders[_addedHeader.Key]; + value.ShouldNotBe(default(StringValues), $"Value of header {_addedHeader.Key} was expected to not be null."); + value.ToString().ShouldBe(_addedHeader.Value); + } + + private void ThenTheHeaderGetsTakenOverToTheRequestHeaders(string expected) + { + var requestHeaders = _context.Request.Headers; + var value = requestHeaders[_addedHeader.Key]; + value.ToString().ShouldBe(expected); + } + } +} diff --git a/test/Ocelot.UnitTests/Infrastructure/HttpDataRepositoryTests.cs b/test/Ocelot.UnitTests/Infrastructure/HttpDataRepositoryTests.cs index 0effbed5a..6eb08d94a 100644 --- a/test/Ocelot.UnitTests/Infrastructure/HttpDataRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Infrastructure/HttpDataRepositoryTests.cs @@ -1,88 +1,88 @@ -using Microsoft.AspNetCore.Http; -using Ocelot.Infrastructure.RequestData; -using Ocelot.Responses; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Infrastructure -{ - public class HttpDataRepositoryTests - { - private readonly HttpContext _httpContext; - private IHttpContextAccessor _httpContextAccessor; - private readonly HttpDataRepository _httpDataRepository; - private object _result; - - public HttpDataRepositoryTests() - { - _httpContext = new DefaultHttpContext(); - _httpContextAccessor = new HttpContextAccessor { HttpContext = _httpContext }; - _httpDataRepository = new HttpDataRepository(_httpContextAccessor); - } - - /* - TODO - Additional tests -> Type mistmatch aka Add string, request int - TODO - Additional tests -> HttpContent null. This should never happen - */ - - [Fact] - public void get_returns_correct_key_from_http_context() - { - this.Given(x => x.GivenAHttpContextContaining("key", "string")) - .When(x => x.GetIsCalledWithKey("key")) - .Then(x => x.ThenTheResultIsAnOkResponse("string")) - .BDDfy(); - } - - [Fact] - public void get_returns_error_response_if_the_key_is_not_found() //Therefore does not return null - { - this.Given(x => x.GivenAHttpContextContaining("key", "string")) - .When(x => x.GetIsCalledWithKey("keyDoesNotExist")) - .Then(x => x.ThenTheResultIsAnErrorReposnse("string1")) - .BDDfy(); - } - - [Fact] - public void should_update() - { - this.Given(x => x.GivenAHttpContextContaining("key", "string")) - .And(x => x.UpdateIsCalledWith("key", "new string")) - .When(x => x.GetIsCalledWithKey("key")) - .Then(x => x.ThenTheResultIsAnOkResponse("new string")) - .BDDfy(); - } - - private void UpdateIsCalledWith(string key, string value) - { - _httpDataRepository.Update(key, value); - } - - private void GivenAHttpContextContaining(string key, object o) - { - _httpContext.Items.Add(key, o); - } - - private void GetIsCalledWithKey(string key) - { - _result = _httpDataRepository.Get(key); - } - - private void ThenTheResultIsAnErrorReposnse(object resultValue) - { - _result.ShouldBeOfType>(); - ((ErrorResponse)_result).Data.ShouldBeNull(); - ((ErrorResponse)_result).IsError.ShouldBe(true); - ((ErrorResponse)_result).Errors.ShouldHaveSingleItem() - .ShouldBeOfType() - .Message.ShouldStartWith("Unable to find data for key: "); - } - - private void ThenTheResultIsAnOkResponse(object resultValue) - { - _result.ShouldBeOfType>(); - ((OkResponse)_result).Data.ShouldBe(resultValue); - } - } +using Microsoft.AspNetCore.Http; +using Ocelot.Infrastructure.RequestData; +using Ocelot.Responses; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Infrastructure +{ + public class HttpDataRepositoryTests + { + private readonly HttpContext _httpContext; + private IHttpContextAccessor _httpContextAccessor; + private readonly HttpDataRepository _httpDataRepository; + private object _result; + + public HttpDataRepositoryTests() + { + _httpContext = new DefaultHttpContext(); + _httpContextAccessor = new HttpContextAccessor { HttpContext = _httpContext }; + _httpDataRepository = new HttpDataRepository(_httpContextAccessor); + } + + /* + TODO - Additional tests -> Type mistmatch aka Add string, request int + TODO - Additional tests -> HttpContent null. This should never happen + */ + + [Fact] + public void get_returns_correct_key_from_http_context() + { + this.Given(x => x.GivenAHttpContextContaining("key", "string")) + .When(x => x.GetIsCalledWithKey("key")) + .Then(x => x.ThenTheResultIsAnOkResponse("string")) + .BDDfy(); + } + + [Fact] + public void get_returns_error_response_if_the_key_is_not_found() //Therefore does not return null + { + this.Given(x => x.GivenAHttpContextContaining("key", "string")) + .When(x => x.GetIsCalledWithKey("keyDoesNotExist")) + .Then(x => x.ThenTheResultIsAnErrorReposnse("string1")) + .BDDfy(); + } + + [Fact] + public void should_update() + { + this.Given(x => x.GivenAHttpContextContaining("key", "string")) + .And(x => x.UpdateIsCalledWith("key", "new string")) + .When(x => x.GetIsCalledWithKey("key")) + .Then(x => x.ThenTheResultIsAnOkResponse("new string")) + .BDDfy(); + } + + private void UpdateIsCalledWith(string key, string value) + { + _httpDataRepository.Update(key, value); + } + + private void GivenAHttpContextContaining(string key, object o) + { + _httpContext.Items.Add(key, o); + } + + private void GetIsCalledWithKey(string key) + { + _result = _httpDataRepository.Get(key); + } + + private void ThenTheResultIsAnErrorReposnse(object resultValue) + { + _result.ShouldBeOfType>(); + ((ErrorResponse)_result).Data.ShouldBe(default(T)); + ((ErrorResponse)_result).IsError.ShouldBe(true); + ((ErrorResponse)_result).Errors.ShouldHaveSingleItem() + .ShouldBeOfType() + .Message.ShouldStartWith("Unable to find data for key: "); + } + + private void ThenTheResultIsAnOkResponse(object resultValue) + { + _result.ShouldBeOfType>(); + ((OkResponse)_result).Data.ShouldBe(resultValue); + } + } } diff --git a/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs b/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs index b3f0193cb..855388b19 100644 --- a/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs +++ b/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs @@ -1,149 +1,154 @@ -using KubeClient; -using KubeClient.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Moq; -using Newtonsoft.Json; -using Ocelot.Logging; -using Ocelot.Provider.Kubernetes; -using Ocelot.Values; -using Shouldly; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Kubernetes -{ - public class KubeServiceDiscoveryProviderTests : IDisposable - { - private IWebHost _fakeKubeBuilder; - private readonly KubernetesServiceDiscoveryProvider _provider; - private EndpointsV1 _endpointEntries; - private readonly string _serviceName; - private readonly string _namespaces; - private readonly int _port; - private readonly string _kubeHost; - private readonly string _fakekubeServiceDiscoveryUrl; - private List _services; - private readonly Mock _factory; - private readonly Mock _logger; - private string _receivedToken; - private readonly IKubeApiClient _clientFactory; - - public KubeServiceDiscoveryProviderTests() - { - _serviceName = "test"; - _namespaces = "dev"; - _port = 86; - _kubeHost = "localhost"; - _fakekubeServiceDiscoveryUrl = $"http://{_kubeHost}:{_port}"; - _endpointEntries = new EndpointsV1(); - _factory = new Mock(); - - var option = new KubeClientOptions - { - ApiEndPoint = new Uri(_fakekubeServiceDiscoveryUrl), - AccessToken = "txpc696iUhbVoudg164r93CxDTrKRVWG", - AuthStrategy = KubeClient.KubeAuthStrategy.BearerToken, - AllowInsecure = true, - }; - - _clientFactory = KubeApiClient.Create(option); - _logger = new Mock(); - _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - var config = new KubeRegistryConfiguration() - { - KeyOfServiceInK8s = _serviceName, - KubeNamespace = _namespaces, - }; - _provider = new KubernetesServiceDiscoveryProvider(config, _factory.Object, _clientFactory); - } - - [Fact] - public void should_return_service_from_k8s() - { - var token = "Bearer txpc696iUhbVoudg164r93CxDTrKRVWG"; - var endPointEntryOne = new EndpointsV1 - { - Kind = "endpoint", - ApiVersion = "1.0", - Metadata = new ObjectMetaV1() - { - Namespace = "dev", - }, - }; - var endpointSubsetV1 = new EndpointSubsetV1(); - endpointSubsetV1.Addresses.Add(new EndpointAddressV1() - { - Ip = "127.0.0.1", - Hostname = "localhost", - }); - endpointSubsetV1.Ports.Add(new EndpointPortV1() - { - Port = 80, - }); - endPointEntryOne.Subsets.Add(endpointSubsetV1); - - this.Given(x => GivenThereIsAFakeKubeServiceDiscoveryProvider(_fakekubeServiceDiscoveryUrl, _serviceName, _namespaces)) - .And(x => GivenTheServicesAreRegisteredWithKube(endPointEntryOne)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(1)) - .And(_ => _receivedToken.ShouldBe(token)) - .BDDfy(); - } - - private void ThenTheCountIs(int count) - { - _services.Count.ShouldBe(count); +using KubeClient; +using KubeClient.Models; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Moq; +using Newtonsoft.Json; +using Ocelot.Logging; +using Ocelot.Provider.Kubernetes; +using Ocelot.Values; +using Shouldly; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Kubernetes +{ + public class KubeServiceDiscoveryProviderTests : IDisposable + { + private IWebHost _fakeKubeBuilder; + private readonly KubernetesServiceDiscoveryProvider _provider; + private EndpointsV1 _endpointEntries; + private readonly string _serviceName; + private readonly string _namespaces; + private readonly int _port; + private readonly string _kubeHost; + private readonly string _fakekubeServiceDiscoveryUrl; + private List _services; + private readonly Mock _factory; + private readonly Mock _logger; + private string _receivedToken; + private readonly IKubeApiClient _clientFactory; + + public KubeServiceDiscoveryProviderTests() + { + _serviceName = "test"; + _namespaces = "dev"; + _port = 86; + _kubeHost = "localhost"; + _fakekubeServiceDiscoveryUrl = $"http://{_kubeHost}:{_port}"; + _endpointEntries = new EndpointsV1(); + _factory = new Mock(); + + var option = new KubeClientOptions + { + ApiEndPoint = new Uri(_fakekubeServiceDiscoveryUrl), + AccessToken = "txpc696iUhbVoudg164r93CxDTrKRVWG", + AuthStrategy = KubeClient.KubeAuthStrategy.BearerToken, + AllowInsecure = true, + }; + + _clientFactory = KubeApiClient.Create(option); + _logger = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + var config = new KubeRegistryConfiguration() + { + KeyOfServiceInK8s = _serviceName, + KubeNamespace = _namespaces, + }; + _provider = new KubernetesServiceDiscoveryProvider(config, _factory.Object, _clientFactory); + } + + [Fact] + public void should_return_service_from_k8s() + { + var token = "Bearer txpc696iUhbVoudg164r93CxDTrKRVWG"; + var endPointEntryOne = new EndpointsV1 + { + Kind = "endpoint", + ApiVersion = "1.0", + Metadata = new ObjectMetaV1() + { + Namespace = "dev", + }, + }; + var endpointSubsetV1 = new EndpointSubsetV1(); + endpointSubsetV1.Addresses.Add(new EndpointAddressV1() + { + Ip = "127.0.0.1", + Hostname = "localhost", + }); + endpointSubsetV1.Ports.Add(new EndpointPortV1() + { + Port = 80, + }); + endPointEntryOne.Subsets.Add(endpointSubsetV1); + + this.Given(x => GivenThereIsAFakeKubeServiceDiscoveryProvider(_fakekubeServiceDiscoveryUrl, _serviceName, _namespaces)) + .And(x => GivenTheServicesAreRegisteredWithKube(endPointEntryOne)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(1)) + .And(_ => ThenTheTokenIs(token)) + .BDDfy(); } - private void WhenIGetTheServices() + private void ThenTheTokenIs(string token) { - _services = _provider.Get().GetAwaiter().GetResult(); - } - - private void GivenTheServicesAreRegisteredWithKube(EndpointsV1 endpointEntries) - { - _endpointEntries = endpointEntries; - } - - private void GivenThereIsAFakeKubeServiceDiscoveryProvider(string url, string serviceName, string namespaces) - { - _fakeKubeBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - if (context.Request.Path.Value == $"/api/v1/namespaces/{namespaces}/endpoints/{serviceName}") - { - if (context.Request.Headers.TryGetValue("Authorization", out var values)) - { - _receivedToken = values.First(); - } - - var json = JsonConvert.SerializeObject(_endpointEntries); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - }) - .Build(); - - _fakeKubeBuilder.Start(); - } - - public void Dispose() - { - _fakeKubeBuilder?.Dispose(); - } - } -} + _receivedToken.ShouldBe(token); + } + + private void ThenTheCountIs(int count) + { + _services.Count.ShouldBe(count); + } + + private void WhenIGetTheServices() + { + _services = _provider.Get().GetAwaiter().GetResult(); + } + + private void GivenTheServicesAreRegisteredWithKube(EndpointsV1 endpointEntries) + { + _endpointEntries = endpointEntries; + } + + private void GivenThereIsAFakeKubeServiceDiscoveryProvider(string url, string serviceName, string namespaces) + { + _fakeKubeBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + if (context.Request.Path.Value == $"/api/v1/namespaces/{namespaces}/endpoints/{serviceName}") + { + if (context.Request.Headers.TryGetValue("Authorization", out var values)) + { + _receivedToken = values.First(); + } + + var json = JsonConvert.SerializeObject(_endpointEntries); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + }) + .Build(); + + _fakeKubeBuilder.Start(); + } + + public void Dispose() + { + _fakeKubeBuilder?.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 22888f7ba..5b8551e35 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -54,7 +54,7 @@ all - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -67,13 +67,13 @@ - + - + diff --git a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs index ad930730f..ddb475bcf 100644 --- a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs +++ b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs @@ -452,7 +452,7 @@ private void ThenTheMappedRequestHasEachHeader() foreach (var header in _mappedRequest.Data.Headers) { var inputHeader = _inputHeaders.First(h => h.Key == header.Key); - inputHeader.ShouldNotBeNull(); + inputHeader.ShouldNotBe(default(KeyValuePair)); inputHeader.Value.Count().ShouldBe(header.Value.Count()); foreach (var inputHeaderValue in inputHeader.Value) { From 17b0555f558e26fdafa6771541ffc471e9e8068c Mon Sep 17 00:00:00 2001 From: TomPallister Date: Mon, 30 Nov 2020 11:27:37 +0000 Subject: [PATCH 28/34] tests passing --- .../EurekaProviderFactory.cs | 38 +- .../Ocelot.Provider.Eureka.csproj | 1 + .../EurekaServiceDiscoveryTests.cs | 100 +-- .../ConsulServiceDiscoveryProviderTests.cs | 596 +++++++++--------- 4 files changed, 368 insertions(+), 367 deletions(-) diff --git a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs index d347f0af8..13e6fc8f8 100644 --- a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs +++ b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs @@ -1,21 +1,21 @@ -namespace Ocelot.Provider.Eureka -{ - using Microsoft.Extensions.DependencyInjection; - using Ocelot.ServiceDiscovery; +namespace Ocelot.Provider.Eureka +{ + using Microsoft.Extensions.DependencyInjection; + using Ocelot.ServiceDiscovery; using Steeltoe.Discovery; - public static class EurekaProviderFactory - { - public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) => - { - var client = provider.GetService(); - - if (config.Type?.ToLower() == "eureka" && client != null) - { - return new Eureka(route.ServiceName, client); - } - - return null; - }; - } -} + public static class EurekaProviderFactory + { + public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) => + { + var client = provider.GetService(); + + if (config.Type?.ToLower() == "eureka" && client != null) + { + return new Eureka(route.ServiceName, client); + } + + return null; + }; + } +} diff --git a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj index ea7275040..3581464d9 100644 --- a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj +++ b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj @@ -28,6 +28,7 @@ + all diff --git a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs index dedb32bd3..5f984a272 100644 --- a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs @@ -1,6 +1,6 @@ namespace Ocelot.AcceptanceTests { - using Configuration.File; + using Ocelot.Configuration.File; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using Steeltoe.Common.Discovery; @@ -38,24 +38,24 @@ public void should_use_eureka_service_discovery_and_make_request() var configuration = new FileConfiguration { Routes = new List + { + new FileRoute { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = serviceName, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - } + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = serviceName, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, }, + }, GlobalConfiguration = new FileGlobalConfiguration() { ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() { - Type = "Eureka" - } - } + Type = "Eureka", + }, + }, }; this.Given(x => x.GivenEurekaProductServiceOneIsRunning(downstreamServiceOneUrl)) @@ -91,42 +91,42 @@ private void GivenThereIsAFakeEurekaServiceDiscoveryProvider(string url, string { name = serviceName, instance = new List + { + new Instance + { + instanceId = $"{serviceInstance.Host}:{serviceInstance}", + hostName = serviceInstance.Host, + app = serviceName, + ipAddr = "127.0.0.1", + status = "UP", + overriddenstatus = "UNKNOWN", + port = new Port {value = serviceInstance.Port, enabled = "true"}, + securePort = new SecurePort {value = serviceInstance.Port, enabled = "true"}, + countryId = 1, + dataCenterInfo = new DataCenterInfo {value = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", name = "MyOwn"}, + leaseInfo = new LeaseInfo + { + renewalIntervalInSecs = 30, + durationInSecs = 90, + registrationTimestamp = 1457714988223, + lastRenewalTimestamp= 1457716158319, + evictionTimestamp = 0, + serviceUpTimestamp = 1457714988223, + }, + metadata = new Metadata { - new Instance - { - instanceId = $"{serviceInstance.Host}:{serviceInstance}", - hostName = serviceInstance.Host, - app = serviceName, - ipAddr = "127.0.0.1", - status = "UP", - overriddenstatus = "UNKNOWN", - port = new Port {value = serviceInstance.Port, enabled = "true"}, - securePort = new SecurePort {value = serviceInstance.Port, enabled = "true"}, - countryId = 1, - dataCenterInfo = new DataCenterInfo {value = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", name = "MyOwn"}, - leaseInfo = new LeaseInfo - { - renewalIntervalInSecs = 30, - durationInSecs = 90, - registrationTimestamp = 1457714988223, - lastRenewalTimestamp= 1457716158319, - evictionTimestamp = 0, - serviceUpTimestamp = 1457714988223 - }, - metadata = new Metadata - { - value = "java.util.Collections$EmptyMap" - }, - homePageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", - statusPageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", - healthCheckUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", - vipAddress = serviceName, - isCoordinatingDiscoveryServer = "false", - lastUpdatedTimestamp = "1457714988223", - lastDirtyTimestamp = "1457714988172", - actionType = "ADDED" - } - } + value = "java.util.Collections$EmptyMap", + }, + homePageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", + statusPageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", + healthCheckUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", + vipAddress = serviceName, + isCoordinatingDiscoveryServer = "false", + lastUpdatedTimestamp = "1457714988223", + lastDirtyTimestamp = "1457714988172", + actionType = "ADDED", + }, + }, }; apps.Add(a); @@ -138,8 +138,8 @@ private void GivenThereIsAFakeEurekaServiceDiscoveryProvider(string url, string { application = apps, apps__hashcode = "UP_1_", - versions__delta = "1" - } + versions__delta = "1", + }, }; var json = JsonConvert.SerializeObject(applications); diff --git a/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs b/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs index 8c385e2bd..6e46ba768 100644 --- a/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs +++ b/test/Ocelot.UnitTests/Consul/ConsulServiceDiscoveryProviderTests.cs @@ -1,304 +1,304 @@ -namespace Ocelot.UnitTests.Consul -{ - using global::Consul; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Moq; - using Newtonsoft.Json; - using Ocelot.Logging; - using Provider.Consul; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using TestStack.BDDfy; - using Values; - using Xunit; - - public class ConsulServiceDiscoveryProviderTests : IDisposable - { - private IWebHost _fakeConsulBuilder; - private readonly List _serviceEntries; - private Consul _provider; - private readonly string _serviceName; - private readonly int _port; - private readonly string _consulHost; - private readonly string _consulScheme; - private readonly string _fakeConsulServiceDiscoveryUrl; - private List _services; - private readonly Mock _factory; - private readonly Mock _logger; - private string _receivedToken; - private readonly IConsulClientFactory _clientFactory; - - public ConsulServiceDiscoveryProviderTests() - { - _serviceName = "test"; - _port = 8500; - _consulHost = "localhost"; - _consulScheme = "http"; - _fakeConsulServiceDiscoveryUrl = $"{_consulScheme}://{_consulHost}:{_port}"; - _serviceEntries = new List(); - _factory = new Mock(); - _clientFactory = new ConsulClientFactory(); - _logger = new Mock(); - _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, null); - _provider = new Consul(config, _factory.Object, _clientFactory); - } - - [Fact] - public void should_return_service_from_consul() - { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(1)) - .BDDfy(); - } - - [Fact] - public void should_use_token() - { - var token = "test token"; - var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, token); - _provider = new Consul(config, _factory.Object, _clientFactory); - - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0], - }, - }; - - this.Given(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .When(_ => WhenIGetTheServices()) - .Then(_ => ThenTheCountIs(1)) - .And(_ => ThenTheTokenIs(token)) - .BDDfy(); +namespace Ocelot.UnitTests.Consul +{ + using global::Consul; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Moq; + using Newtonsoft.Json; + using Ocelot.Logging; + using Provider.Consul; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using TestStack.BDDfy; + using Values; + using Xunit; + + public class ConsulServiceDiscoveryProviderTests : IDisposable + { + private IWebHost _fakeConsulBuilder; + private readonly List _serviceEntries; + private Consul _provider; + private readonly string _serviceName; + private readonly int _port; + private readonly string _consulHost; + private readonly string _consulScheme; + private readonly string _fakeConsulServiceDiscoveryUrl; + private List _services; + private readonly Mock _factory; + private readonly Mock _logger; + private string _receivedToken; + private readonly IConsulClientFactory _clientFactory; + + public ConsulServiceDiscoveryProviderTests() + { + _serviceName = "test"; + _port = 8500; + _consulHost = "localhost"; + _consulScheme = "http"; + _fakeConsulServiceDiscoveryUrl = $"{_consulScheme}://{_consulHost}:{_port}"; + _serviceEntries = new List(); + _factory = new Mock(); + _clientFactory = new ConsulClientFactory(); + _logger = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, null); + _provider = new Consul(config, _factory.Object, _clientFactory); + } + + [Fact] + public void should_return_service_from_consul() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(1)) + .BDDfy(); + } + + [Fact] + public void should_use_token() + { + var token = "test token"; + var config = new ConsulRegistryConfiguration(_consulScheme, _consulHost, _port, _serviceName, token); + _provider = new Consul(config, _factory.Object, _clientFactory); + + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0], + }, + }; + + this.Given(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .When(_ => WhenIGetTheServices()) + .Then(_ => ThenTheCountIs(1)) + .And(_ => ThenTheTokenIs(token)) + .BDDfy(); + } + + [Fact] + public void should_not_return_services_with_invalid_address() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "http://localhost", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "http://localhost", + Port = 50888, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(0)) + .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()) + .BDDfy(); + } + + [Fact] + public void should_not_return_services_with_empty_address() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "", + Port = 50881, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = null, + Port = 50888, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(0)) + .And(x => ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress()) + .BDDfy(); + } + + [Fact] + public void should_not_return_services_with_invalid_port() + { + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = -1, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = _serviceName, + Address = "localhost", + Port = 0, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) + .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .When(x => WhenIGetTheServices()) + .Then(x => ThenTheCountIs(0)) + .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()) + .BDDfy(); + } + + private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress() + { + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + } + + private void ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress() + { + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + } + + private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts() + { + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + + _logger.Verify( + x => x.LogWarning( + "Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), + Times.Once); + } + + private void ThenTheCountIs(int count) + { + _services.Count.ShouldBe(count); } - - [Fact] - public void should_not_return_services_with_invalid_address() - { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "http://localhost", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "http://localhost", - Port = 50888, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(0)) - .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()) - .BDDfy(); - } - - [Fact] - public void should_not_return_services_with_empty_address() - { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "", - Port = 50881, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = null, - Port = 50888, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(0)) - .And(x => ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress()) - .BDDfy(); - } - - [Fact] - public void should_not_return_services_with_invalid_port() - { - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = -1, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = _serviceName, - Address = "localhost", - Port = 0, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName)) - .And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .When(x => WhenIGetTheServices()) - .Then(x => ThenTheCountIs(0)) - .And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()) - .BDDfy(); - } - - private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress() - { - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - } - - private void ThenTheLoggerHasBeenCalledCorrectlyForEmptyAddress() - { - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - } - - private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts() - { - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - - _logger.Verify( - x => x.LogWarning( - "Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"), - Times.Once); - } - - private void ThenTheCountIs(int count) - { - _services.Count.ShouldBe(count); - } - - private void WhenIGetTheServices() - { - _services = _provider.Get().GetAwaiter().GetResult(); + + private void WhenIGetTheServices() + { + _services = _provider.Get().GetAwaiter().GetResult(); } private void ThenTheTokenIs(string token) { _receivedToken.ShouldBe(token); - } - - private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) - { - foreach (var serviceEntry in serviceEntries) - { - _serviceEntries.Add(serviceEntry); - } - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) - { - _fakeConsulBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") - { - if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) - { - _receivedToken = values.First(); - } - - var json = JsonConvert.SerializeObject(_serviceEntries); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - }) - .Build(); - - _fakeConsulBuilder.Start(); - } - - public void Dispose() - { - _fakeConsulBuilder?.Dispose(); - } - } -} + } + + private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) + { + foreach (var serviceEntry in serviceEntries) + { + _serviceEntries.Add(serviceEntry); + } + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + { + _fakeConsulBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + { + if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) + { + _receivedToken = values.First(); + } + + var json = JsonConvert.SerializeObject(_serviceEntries); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + }) + .Build(); + + _fakeConsulBuilder.Start(); + } + + public void Dispose() + { + _fakeConsulBuilder?.Dispose(); + } + } +} From f62ed72dde7fd670228870fb81b5f2ed2fe27ace Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 1 Dec 2020 11:06:49 +0000 Subject: [PATCH 29/34] all packages upgraded and tests passing --- docs/features/administration.rst | 15 +- docs/features/authentication.rst | 8 +- .../Ocelot.Administration.csproj | 2 +- .../OcelotBuilderExtensions.cs | 45 +- .../AuthenticationTests.cs | 21 +- .../AuthorisationTests.cs | 821 ++++---- .../ClaimsToDownstreamPathTests.cs | 417 ++-- .../ClaimsToHeadersForwardingTests.cs | 363 ++-- .../ClaimsToQueryStringForwardingTests.cs | 208 +- .../Ocelot.AcceptanceTests.csproj | 2 +- test/Ocelot.AcceptanceTests/Steps.cs | 4 +- .../AdministrationTests.cs | 1829 +++++++++-------- .../Ocelot.IntegrationTests.csproj | 2 +- .../OcelotAdministrationBuilderTests.cs | 201 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 2 +- 15 files changed, 1998 insertions(+), 1942 deletions(-) diff --git a/docs/features/administration.rst b/docs/features/administration.rst index 274e7299f..6449f583a 100644 --- a/docs/features/administration.rst +++ b/docs/features/administration.rst @@ -12,19 +12,22 @@ This will bring down everything needed by the admin API. Providing your own IdentityServer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method. .. code-block:: csharp public virtual void ConfigureServices(IServiceCollection services) { - Action options = o => { - // o.Authority = ; - // o.ApiName = ; - // etc.... + Action options = o => + { + o.Authority = identityServerRootUrl; + o.RequireHttpsMetadata = false; + o.TokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, }; + // etc.... + }; services .AddOcelot() diff --git a/docs/features/authentication.rst b/docs/features/authentication.rst index f935cbacd..8aeca15f4 100644 --- a/docs/features/authentication.rst +++ b/docs/features/authentication.rst @@ -97,16 +97,14 @@ In order to use IdentityServer bearer tokens, register your IdentityServer servi public void ConfigureServices(IServiceCollection services) { var authenticationProviderKey = "TestKey"; - Action options = o => + Action options = o => { o.Authority = "https://whereyouridentityserverlives.com"; - o.ApiName = "api"; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; + // etc }; services.AddAuthentication() - .AddIdentityServerAuthentication(authenticationProviderKey, options); + .AddJwtBearer(authenticationProviderKey, options); services.AddOcelot(); } diff --git a/src/Ocelot.Administration/Ocelot.Administration.csproj b/src/Ocelot.Administration/Ocelot.Administration.csproj index d44b4b862..e449ce9f3 100644 --- a/src/Ocelot.Administration/Ocelot.Administration.csproj +++ b/src/Ocelot.Administration/Ocelot.Administration.csproj @@ -31,7 +31,7 @@ all - + diff --git a/src/Ocelot.Administration/OcelotBuilderExtensions.cs b/src/Ocelot.Administration/OcelotBuilderExtensions.cs index da25beaa0..cf14fc1e4 100644 --- a/src/Ocelot.Administration/OcelotBuilderExtensions.cs +++ b/src/Ocelot.Administration/OcelotBuilderExtensions.cs @@ -1,7 +1,6 @@ using Ocelot.DependencyInjection; using IdentityServer4.AccessTokenValidation; using IdentityServer4.Models; -using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -10,6 +9,9 @@ using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Cryptography.X509Certificates; +using System.Linq; +using Microsoft.IdentityModel.Tokens; +using Microsoft.AspNetCore.Authentication.JwtBearer; namespace Ocelot.Administration { @@ -18,6 +20,7 @@ public static class OcelotBuilderExtensions public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, string secret) { var administrationPath = new AdministrationPath(path); + builder.Services.AddSingleton(IdentityServerMiddlewareConfigurationProvider.Get); //add identity server for admin area @@ -32,7 +35,7 @@ public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder return new OcelotAdministrationBuilder(builder.Services, builder.Configuration); } - public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action configureOptions) + public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action configureOptions) { var administrationPath = new AdministrationPath(path); builder.Services.AddSingleton(IdentityServerMiddlewareConfigurationProvider.Get); @@ -46,11 +49,11 @@ public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder return new OcelotAdministrationBuilder(builder.Services, builder.Configuration); } - private static void AddIdentityServer(Action configOptions, IOcelotBuilder builder) + private static void AddIdentityServer(Action configOptions, IOcelotBuilder builder) { builder.Services .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) - .AddIdentityServerAuthentication(configOptions); + .AddJwtBearer("Bearer", configOptions); } private static void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath, IOcelotBuilder builder, IConfiguration configuration) @@ -60,7 +63,9 @@ private static void AddIdentityServer(IIdentityServerConfiguration identityServe .AddIdentityServer(o => { o.IssuerUri = "Ocelot"; + o.EmitStaticAudienceClaim = true; }) + .AddInMemoryApiScopes(ApiScopes(identityServerConfiguration)) .AddInMemoryApiResources(Resources(identityServerConfiguration)) .AddInMemoryClients(Client(identityServerConfiguration)); @@ -68,14 +73,17 @@ private static void AddIdentityServer(IIdentityServerConfiguration identityServe var baseSchemeUrlAndPort = urlFinder.Find(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - builder.Services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) - .AddIdentityServerAuthentication(o => + builder.Services + .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) + .AddJwtBearer("Bearer", options => { - o.Authority = baseSchemeUrlAndPort + adminPath.Path; - o.ApiName = identityServerConfiguration.ApiName; - o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = identityServerConfiguration.ApiSecret; + options.Authority = baseSchemeUrlAndPort + adminPath.Path; + options.RequireHttpsMetadata = identityServerConfiguration.RequireHttps; + + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, + }; }); //todo - refactor naming.. @@ -91,6 +99,11 @@ private static void AddIdentityServer(IIdentityServerConfiguration identityServe } } + private static IEnumerable ApiScopes(IIdentityServerConfiguration identityServerConfiguration) + { + return identityServerConfiguration.AllowedScopes.Select(s => new ApiScope(s)); + } + private static List Resources(IIdentityServerConfiguration identityServerConfiguration) { return new List @@ -101,9 +114,9 @@ private static List Resources(IIdentityServerConfiguration identity { new Secret { - Value = identityServerConfiguration.ApiSecret.Sha256() - } - } + Value = identityServerConfiguration.ApiSecret.Sha256(), + }, + }, }, }; } @@ -117,8 +130,8 @@ private static List Client(IIdentityServerConfiguration identityServerCo ClientId = identityServerConfiguration.ApiName, AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = new List {new Secret(identityServerConfiguration.ApiSecret.Sha256())}, - AllowedScopes = { identityServerConfiguration.ApiName } - } + AllowedScopes = identityServerConfiguration.AllowedScopes, + }, }; } } diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index 0211f09b6..5c541ac6f 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -278,6 +278,11 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, string a services.AddLogging(); services.AddIdentityServer() .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(new List + { + new ApiScope(apiName, "test"), + new ApiScope(api2Name, "test"), + }) .AddInMemoryApiResources(new List { new ApiResource @@ -286,12 +291,12 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, string a Description = "My API", Enabled = true, DisplayName = "test", - Scopes = new List() + Scopes = new List() { - new Scope("api"), - new Scope("api.readOnly"), - new Scope("openid"), - new Scope("offline_access"), + "api", + "api.readOnly", + "openid", + "offline_access", }, ApiSecrets = new List() { @@ -311,10 +316,10 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, string a Description = "My second API", Enabled = true, DisplayName = "second test", - Scopes = new List() + Scopes = new List() { - new Scope("api2"), - new Scope("api2.readOnly"), + "api2", + "api2.readOnly", }, ApiSecrets = new List() { diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index 4f8a28dfc..9a9a3dd4c 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -1,91 +1,91 @@ -namespace Ocelot.AcceptanceTests -{ - using IdentityServer4.AccessTokenValidation; - using IdentityServer4.Models; - using IdentityServer4.Test; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Security.Claims; - using TestStack.BDDfy; - using Xunit; - - public class AuthorisationTests : IDisposable - { - private IWebHost _identityServerBuilder; - private readonly Steps _steps; - private readonly Action _options; - private string _identityServerRootUrl; - private readonly ServiceHandler _serviceHandler; - - public AuthorisationTests() - { - _serviceHandler = new ServiceHandler(); +namespace Ocelot.AcceptanceTests +{ + using IdentityServer4.AccessTokenValidation; + using IdentityServer4.Models; + using IdentityServer4.Test; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Security.Claims; + using TestStack.BDDfy; + using Xunit; + + public class AuthorisationTests : IDisposable + { + private IWebHost _identityServerBuilder; + private readonly Steps _steps; + private readonly Action _options; + private string _identityServerRootUrl; + private readonly ServiceHandler _serviceHandler; + + public AuthorisationTests() + { + _serviceHandler = new ServiceHandler(); _steps = new Steps(); var identityServerPort = RandomPortFinder.GetRandomPort(); - _identityServerRootUrl = $"http://localhost:{identityServerPort}"; - _options = o => - { - o.Authority = _identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - } - - [Fact] - public void should_return_response_200_authorising_route() - { + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; + _options = o => + { + o.Authority = _identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + } + + [Fact] + public void should_return_response_200_authorising_route() + { int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration + + var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - }, - AddHeadersToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - AddClaimsToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - RouteClaimsRequirement = - { - {"UserType", "registered"} - } - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + }, + AddHeadersToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"}, + }, + AddClaimsToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"}, + }, + RouteClaimsRequirement = + { + {"UserType", "registered"}, + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) @@ -97,54 +97,54 @@ public void should_return_response_200_authorising_route() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_return_response_403_authorising_route() - { - int port = RandomPortFinder.GetRandomPort(); + .BDDfy(); + } + + [Fact] + public void should_return_response_403_authorising_route() + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - }, - AddHeadersToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - AddClaimsToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - RouteClaimsRequirement = - { - {"UserType", "registered"} - } - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + }, + AddHeadersToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"}, + }, + AddClaimsToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"UserId", "Claims[sub] > value[1] > |"}, + }, + RouteClaimsRequirement = + { + {"UserType", "registered"}, + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) @@ -155,39 +155,39 @@ public void should_return_response_403_authorising_route() .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_using_identity_server_with_allowed_scope() - { - int port = RandomPortFinder.GetRandomPort(); - + .BDDfy(); + } + + [Fact] + public void should_return_response_200_using_identity_server_with_allowed_scope() + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List{ "api", "api.readOnly", "openid", "offline_access" }, - }, - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List{ "api", "api.readOnly", "openid", "offline_access" }, + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) @@ -198,39 +198,39 @@ public void should_return_response_200_using_identity_server_with_allowed_scope( .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_response_403_using_identity_server_with_scope_not_allowed() - { - int port = RandomPortFinder.GetRandomPort(); - + .BDDfy(); + } + + [Fact] + public void should_return_response_403_using_identity_server_with_scope_not_allowed() + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List{ "api", "openid", "offline_access" }, - }, - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List{ "api", "openid", "offline_access" }, + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) @@ -241,57 +241,57 @@ public void should_return_response_403_using_identity_server_with_scope_not_allo .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) - .BDDfy(); - } - - [Fact] - public void should_fix_issue_240() - { - int port = RandomPortFinder.GetRandomPort(); - + .BDDfy(); + } + + [Fact] + public void should_fix_issue_240() + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test" - }, - RouteClaimsRequirement = - { - {"Role", "User"} - } - } - } - }; - - var users = new List - { - new TestUser - { - Username = "test", - Password = "test", - SubjectId = "registered|1231231", - Claims = new List - { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + }, + RouteClaimsRequirement = + { + {"Role", "User"}, + }, + }, + }, + }; + + var users = new List + { + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + Claims = new List + { new Claim("Role", "AdminUser"), - new Claim("Role", "User") - }, - } + new Claim("Role", "User"), + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, users)) @@ -303,170 +303,181 @@ public void should_fix_issue_240() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType) - { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = "My API", - Enabled = true, - DisplayName = "test", - Scopes = new List() - { - new Scope("api"), - new Scope("api.readOnly"), - new Scope("openid"), - new Scope("offline_access") - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId", "UserType", "UserId" - } - }, - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = "client", - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "api.readOnly", "openid", "offline_access" }, - AccessTokenType = tokenType, - Enabled = true, - RequireClientSecret = false - } - }) - .AddTestUsers(new List - { - new TestUser - { - Username = "test", - Password = "test", - SubjectId = "registered|1231231", - Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "321") - } - } - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); - - _identityServerBuilder.Start(); - - _steps.VerifyIdentiryServerStarted(url); - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List users) - { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = "My API", - Enabled = true, - DisplayName = "test", - Scopes = new List() - { - new Scope("api"), - new Scope("api.readOnly"), - new Scope("openid"), - new Scope("offline_access"), - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId", "UserType", "UserId", "Role" - } - }, - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = "client", - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "api.readOnly", "openid", "offline_access" }, - AccessTokenType = tokenType, - Enabled = true, - RequireClientSecret = false, - } - }) - .AddTestUsers(users); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); - - _identityServerBuilder.Start(); - - _steps.VerifyIdentiryServerStarted(url); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - _identityServerBuilder?.Dispose(); - } - } + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(new List + { + new ApiScope(apiName, "test"), + new ApiScope("openid", "test"), + new ApiScope("offline_access", "test"), + new ApiScope("api.readOnly", "test"), + }) + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + "api", + "api.readOnly", + "openid", + "offline_access", + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256(), + }, + }, + UserClaims = new List() + { + "CustomerId", "LocationId", "UserType", "UserId", + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "api.readOnly", "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false, + }, + }) + .AddTestUsers(new List + { + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + Claims = new List + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "321"), + }, + }, + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List users) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(new List + { + new ApiScope(apiName, "test"), + }) + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + "api", + "api.readOnly", + "openid", + "offline_access", + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256(), + }, + }, + UserClaims = new List() + { + "CustomerId", "LocationId", "UserType", "UserId", "Role", + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "api.readOnly", "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false, + }, + }) + .AddTestUsers(users); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + _identityServerBuilder?.Dispose(); + } + } } diff --git a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs index 47238e88a..3bb37848f 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs @@ -1,210 +1,217 @@ -using Xunit; - -namespace Ocelot.AcceptanceTests -{ - using IdentityServer4.AccessTokenValidation; - using IdentityServer4.Models; - using IdentityServer4.Test; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using TestStack.BDDfy; - - public class ClaimsToDownstreamPathTests : IDisposable - { - private IWebHost _servicebuilder; - private IWebHost _identityServerBuilder; - private readonly Steps _steps; - private Action _options; - private string _identityServerRootUrl; - private string _downstreamFinalPath; - - public ClaimsToDownstreamPathTests() - { - var identityServerPort = RandomPortFinder.GetRandomPort(); - _identityServerRootUrl = $"http://localhost:{identityServerPort}"; - _steps = new Steps(); - _options = o => - { - o.Authority = _identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - } - - [Fact] - public void should_return_200_and_change_downstream_path() - { - var user = new TestUser() - { - Username = "test", - Password = "test", - SubjectId = "registered|1231231", - }; - - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/users/{userId}", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/users", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List - { - "openid", "offline_access", "api", - }, - }, - ChangeDownstreamPathTemplate = - { - {"userId", "Claims[sub] > value[1] > |"}, - }, - }, - }, - }; - - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) - .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) - .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/users")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231")) - .And(x => ThenTheDownstreamPathIs("/users/1231231")) - .BDDfy(); +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + using IdentityServer4.AccessTokenValidation; + using IdentityServer4.Models; + using IdentityServer4.Test; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using TestStack.BDDfy; + + public class ClaimsToDownstreamPathTests : IDisposable + { + private IWebHost _servicebuilder; + private IWebHost _identityServerBuilder; + private readonly Steps _steps; + private Action _options; + private string _identityServerRootUrl; + private string _downstreamFinalPath; + + public ClaimsToDownstreamPathTests() + { + var identityServerPort = RandomPortFinder.GetRandomPort(); + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; + _steps = new Steps(); + _options = o => + { + o.Authority = _identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + } + + [Fact] + public void should_return_200_and_change_downstream_path() + { + var user = new TestUser() + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + }; + + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/users/{userId}", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/users", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api", + }, + }, + ChangeDownstreamPathTemplate = + { + {"userId", "Claims[sub] > value[1] > |"}, + }, + }, + }, + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/users")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231")) + .And(x => ThenTheDownstreamPathIs("/users/1231231")) + .BDDfy(); } private void ThenTheDownstreamPathIs(string path) { _downstreamFinalPath.ShouldBe(path); - } - - private void GivenThereIsAServiceRunningOn(string url, int statusCode) - { - _servicebuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - _downstreamFinalPath = context.Request.Path.Value; - - string userId = _downstreamFinalPath.Replace("/users/", string.Empty); - - var responseBody = $"UserId: {userId}"; - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }) - .Build(); - - _servicebuilder.Start(); - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) - { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = "My API", - Enabled = true, - DisplayName = "test", - Scopes = new List() - { - new Scope("api"), - new Scope("openid"), - new Scope("offline_access") - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId", "UserType", "UserId" - } - } - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = "client", - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "openid", "offline_access" }, - AccessTokenType = tokenType, - Enabled = true, - RequireClientSecret = false - } - }) - .AddTestUsers(new List - { - user - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); - - _identityServerBuilder.Start(); - - _steps.VerifyIdentiryServerStarted(url); - } - - public void Dispose() - { - _servicebuilder?.Dispose(); - _steps.Dispose(); - _identityServerBuilder?.Dispose(); - } - } -} + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode) + { + _servicebuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + _downstreamFinalPath = context.Request.Path.Value; + + string userId = _downstreamFinalPath.Replace("/users/", string.Empty); + + var responseBody = $"UserId: {userId}"; + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _servicebuilder.Start(); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(new List + { + new ApiScope(apiName, "test"), + new ApiScope("openid", "test"), + new ApiScope("offline_access", "test"), + new ApiScope("api.readOnly", "test"), + }) + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + "api", + "openid", + "offline_access", + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256(), + }, + }, + UserClaims = new List() + { + "CustomerId", "LocationId", "UserType", "UserId", + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false, + }, + }) + .AddTestUsers(new List + { + user, + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + public void Dispose() + { + _servicebuilder?.Dispose(); + _steps.Dispose(); + _identityServerBuilder?.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs index 2712e2b79..711450928 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs @@ -1,101 +1,101 @@ -using Xunit; - -[assembly: CollectionBehavior(DisableTestParallelization = true)] - -namespace Ocelot.AcceptanceTests -{ - using IdentityServer4.AccessTokenValidation; - using IdentityServer4.Models; - using IdentityServer4.Test; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net; - using System.Security.Claims; - using TestStack.BDDfy; - - public class ClaimsToHeadersForwardingTests : IDisposable - { - private IWebHost _identityServerBuilder; - private readonly Steps _steps; - private Action _options; - private string _identityServerRootUrl; - private readonly ServiceHandler _serviceHandler; - - public ClaimsToHeadersForwardingTests() - { - _serviceHandler = new ServiceHandler(); +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] + +namespace Ocelot.AcceptanceTests +{ + using IdentityServer4.AccessTokenValidation; + using IdentityServer4.Models; + using IdentityServer4.Test; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net; + using System.Security.Claims; + using TestStack.BDDfy; + + public class ClaimsToHeadersForwardingTests : IDisposable + { + private IWebHost _identityServerBuilder; + private readonly Steps _steps; + private Action _options; + private string _identityServerRootUrl; + private readonly ServiceHandler _serviceHandler; + + public ClaimsToHeadersForwardingTests() + { + _serviceHandler = new ServiceHandler(); _steps = new Steps(); var identityServerPort = RandomPortFinder.GetRandomPort(); - _identityServerRootUrl = $"http://localhost:{identityServerPort}"; - _options = o => - { - o.Authority = _identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - } - - [Fact] - public void should_return_response_200_and_foward_claim_as_header() + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; + _options = o => + { + o.Authority = _identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + } + + [Fact] + public void should_return_response_200_and_foward_claim_as_header() { var user = new TestUser() { Username = "test", Password = "test", SubjectId = "registered|1231231", - Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "1") - } + Claims = new List + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "1"), + }, }; int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List - { - "openid", "offline_access", "api" - }, - }, - AddHeadersToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - } - } - } + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api", + }, + }, + AddHeadersToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"}, + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) @@ -107,98 +107,105 @@ public void should_return_response_200_and_foward_claim_as_header() .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string url, int statusCode) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First(); - var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First(); - var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First(); - var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First(); - - var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}"; - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) - { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = "My API", - Enabled = true, - DisplayName = "test", - Scopes = new List() - { - new Scope("api"), - new Scope("openid"), - new Scope("offline_access") - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId", "UserType", "UserId" - } - } - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = "client", - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "openid", "offline_access" }, - AccessTokenType = tokenType, - Enabled = true, - RequireClientSecret = false - } - }) - .AddTestUsers(new List - { - user - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); - - _identityServerBuilder.Start(); - - _steps.VerifyIdentiryServerStarted(url); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - _identityServerBuilder?.Dispose(); - } - } + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First(); + var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First(); + var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First(); + var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First(); + + var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}"; + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(new List + { + new ApiScope(apiName, "test"), + new ApiScope("openid", "test"), + new ApiScope("offline_access", "test"), + new ApiScope("api.readOnly", "test"), + }) + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + "api", + "openid", + "offline_access", + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256(), + }, + }, + UserClaims = new List() + { + "CustomerId", "LocationId", "UserType", "UserId", + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false, + }, + }) + .AddTestUsers(new List + { + user, + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + _identityServerBuilder?.Dispose(); + } + } } diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index 978a54611..22285e153 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -1,23 +1,22 @@ -using IdentityServer4.AccessTokenValidation; -using IdentityServer4.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Primitives; -using Ocelot.Configuration.File; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Security.Claims; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.AcceptanceTests +namespace Ocelot.AcceptanceTests { using IdentityServer4.Test; using Shouldly; + using IdentityServer4.AccessTokenValidation; + using IdentityServer4.Models; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Primitives; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Security.Claims; + using TestStack.BDDfy; + using Xunit; public class ClaimsToQueryStringForwardingTests : IDisposable { @@ -52,10 +51,10 @@ public void should_return_response_200_and_foward_claim_as_query_string() Password = "test", SubjectId = "registered|1231231", Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "1") - } + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "1"), + }, }; int port = RandomPortFinder.GetRandomPort(); @@ -63,38 +62,38 @@ public void should_return_response_200_and_foward_claim_as_query_string() var configuration = new FileConfiguration { Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List - { - "openid", "offline_access", "api" - }, - }, - AddQueriesToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - } - } - } + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api", + }, + }, + AddQueriesToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"}, + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) @@ -118,10 +117,10 @@ public void should_return_response_200_and_foward_claim_as_query_string_and_pres Password = "test", SubjectId = "registered|1231231", Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "1") - } + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "1"), + }, }; int port = RandomPortFinder.GetRandomPort(); @@ -129,38 +128,38 @@ public void should_return_response_200_and_foward_claim_as_query_string_and_pres var configuration = new FileConfiguration { Routes = new List - { - new FileRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List - { - "openid", "offline_access", "api" - }, - }, - AddQueriesToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - } - } - } + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api", + }, + }, + AddQueriesToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"}, + }, + }, + }, }; this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) @@ -230,6 +229,13 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo services.AddLogging(); services.AddIdentityServer() .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(new List + { + new ApiScope(apiName, "test"), + new ApiScope("openid", "test"), + new ApiScope("offline_access", "test"), + new ApiScope("api.readOnly", "test"), + }) .AddInMemoryApiResources(new List { new ApiResource @@ -238,24 +244,24 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo Description = "My API", Enabled = true, DisplayName = "test", - Scopes = new List() + Scopes = new List() { - new Scope("api"), - new Scope("openid"), - new Scope("offline_access") + "api", + "openid", + "offline_access", }, ApiSecrets = new List() { new Secret { - Value = "secret".Sha256() - } + Value = "secret".Sha256(), + }, }, UserClaims = new List() { - "CustomerId", "LocationId", "UserType", "UserId" - } - } + "CustomerId", "LocationId", "UserType", "UserId", + }, + }, }) .AddInMemoryClients(new List { @@ -267,12 +273,12 @@ private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTo AllowedScopes = new List { apiName, "openid", "offline_access" }, AccessTokenType = tokenType, Enabled = true, - RequireClientSecret = false - } + RequireClientSecret = false, + }, }) .AddTestUsers(new List { - user + user, }); }) .Configure(app => diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 850f8276b..e09db82d3 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -64,7 +64,7 @@ - + diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 453303117..233be3c1a 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -814,7 +814,7 @@ public void GivenIHaveAToken(string url) new KeyValuePair("scope", "api"), new KeyValuePair("username", "test"), new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") + new KeyValuePair("grant_type", "password"), }; var content = new FormUrlEncodedContent(formData); @@ -837,7 +837,7 @@ public void GivenIHaveATokenForApiReadOnlyScope(string url) new KeyValuePair("scope", "api.readOnly"), new KeyValuePair("username", "test"), new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") + new KeyValuePair("grant_type", "password"), }; var content = new FormUrlEncodedContent(formData); diff --git a/test/Ocelot.IntegrationTests/AdministrationTests.cs b/test/Ocelot.IntegrationTests/AdministrationTests.cs index d45bac237..8b39c9cb4 100644 --- a/test/Ocelot.IntegrationTests/AdministrationTests.cs +++ b/test/Ocelot.IntegrationTests/AdministrationTests.cs @@ -1,912 +1,917 @@ -using IdentityServer4.AccessTokenValidation; -using IdentityServer4.Models; -using IdentityServer4.Test; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Newtonsoft.Json; -using Ocelot.Administration; -using Ocelot.Cache; -using Ocelot.Configuration.File; -using Ocelot.DependencyInjection; -using Ocelot.Middleware; -using Shouldly; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using TestStack.BDDfy; -using Ocelot.Configuration.ChangeTracking; -using Xunit; - -namespace Ocelot.IntegrationTests -{ - public class AdministrationTests : IDisposable - { - private HttpClient _httpClient; - private readonly HttpClient _httpClientTwo; - private HttpResponseMessage _response; - private IHost _builder; - private IHostBuilder _webHostBuilder; - private string _ocelotBaseUrl; - private BearerToken _token; - private IHostBuilder _webHostBuilderTwo; - private IHost _builderTwo; - private IHost _identityServerBuilder; - private IHost _fooServiceBuilder; - private IHost _barServiceBuilder; - - public AdministrationTests() - { - _httpClient = new HttpClient(); - _httpClientTwo = new HttpClient(); - _ocelotBaseUrl = "http://localhost:5000"; - _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); - } - - [Fact] - public void should_return_response_401_with_call_re_routes_controller() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller_using_base_url_added_in_file_config() - { - _httpClient = new HttpClient(); - _ocelotBaseUrl = "http://localhost:5011"; - _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - BaseUrl = _ocelotBaseUrl - } - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithNoWebHostBuilder(_ocelotBaseUrl)) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet()) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenAnotherOcelotIsRunning("http://localhost:5017")) - .When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_file_configuration() - { - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - RequestIdKey = "RequestId", - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "127.0.0.1", - } - }, - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - Region = "Geoff" - } - }, - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - Region = "Dave" - } - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(configuration)) - .BDDfy(); - } - - [Fact] - public void should_get_file_configuration_edit_and_post_updated_version() - { - var initialConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test" - } - }, - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.123.123", - Port = 443, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = new List { "post" }, - UpstreamPathTemplate = "/test" - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration)) - .BDDfy(); - } - - [Fact] - public void should_activate_change_token_when_configuration_is_updated() - { - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration(), - Routes = new List - { - new FileRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - }, - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - }, - }, - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", configuration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => TheChangeTokenShouldBeActive()) - .And(x => ThenTheResponseShouldBe(configuration)) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .And(x => ThenTheResponseShouldBe(configuration)) - .And(_ => ThenTheConfigurationIsSavedCorrectly(configuration)) - .BDDfy(); - } - - private void TheChangeTokenShouldBeActive() - { - _builder.Services.GetRequiredService().ChangeToken.HasChanged.ShouldBeTrue(); - } - - 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_route() - { - var fooPort = 47689; - var barPort = 27654; - - var initialConfiguration = new FileConfiguration - { - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = fooPort, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/foo", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/foo" - } - } - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = barPort, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/bar", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/foo" - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}")) - .And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}")) - .And(x => GivenOcelotIsRunning()) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("foo")) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("bar")) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(initialConfiguration)) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("foo")) - .BDDfy(); - } - - [Fact] - public void should_clear_region() - { - var initialConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - Routes = new List() - { - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10 - } - }, - new FileRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10 - } - } - } - }; - - var regionToClear = "gettest"; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller_when_using_own_identity_server_to_secure_admin_area() - { - var configuration = new FileConfiguration(); - - var identityServerRootUrl = "http://localhost:5123"; - - Action options = o => - { - o.Authority = identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenThereIsAnIdentityServerOn(identityServerRootUrl, "api")) - .And(x => GivenOcelotIsRunningWithIdentityServerSettings(options)) - .And(x => GivenIHaveAToken(identityServerRootUrl)) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - private void GivenIHaveAToken(string url) - { - var formData = new List> - { - new KeyValuePair("client_id", "api"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "api"), - new KeyValuePair("username", "test"), - new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") - }; - var content = new FormUrlEncodedContent(formData); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.PostAsync($"{url}/connect/token", content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - } - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName) - { - _identityServerBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = apiName, - Enabled = true, - DisplayName = apiName, - Scopes = new List() - { - new Scope(apiName), - }, - }, - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = apiName, - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List { new Secret("secret".Sha256()) }, - AllowedScopes = new List { apiName }, - AccessTokenType = AccessTokenType.Jwt, - Enabled = true - }, - }) - .AddTestUsers(new List - { - new TestUser - { - Username = "test", - Password = "test", - SubjectId = "1231231" - }, - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - } - ); - }).Build(); - - _identityServerBuilder.Start(); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; - response.EnsureSuccessStatusCode(); - } - } - - private void GivenAnotherOcelotIsRunning(string baseUrl) - { - _httpClientTwo.BaseAddress = new Uri(baseUrl); - - _webHostBuilderTwo = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .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(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builderTwo = _webHostBuilderTwo.Build(); - - _builderTwo.Start(); - } - - private void GivenIdentityServerSigningEnvironmentalVariablesAreSet() - { - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx"); - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test"); - } - - private void WhenIGetUrlOnTheSecondOcelot(string url) - { - _httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - _response = _httpClientTwo.GetAsync(url).Result; - } - - private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) - { - var json = JsonConvert.SerializeObject(updatedConfiguration); - var content = new StringContent(json); - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - _response = _httpClient.PostAsync(url, content).Result; - } - - private void ThenTheResponseShouldBe(List expected) - { - var content = _response.Content.ReadAsStringAsync().Result; - var result = JsonConvert.DeserializeObject(content); - result.Value.ShouldBe(expected); - } - - private void ThenTheResponseBodyShouldBe(string expected) - { - var content = _response.Content.ReadAsStringAsync().Result; - content.ShouldBe(expected); - } - - private void ThenTheResponseShouldBe(FileConfiguration expecteds) - { - var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); - - response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); - response.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); - response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); - response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < response.Routes.Count; i++) - { - for (var j = 0; j < response.Routes[i].DownstreamHostAndPorts.Count; j++) - { - var result = response.Routes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.Routes[i].DownstreamHostAndPorts[j]; - result.Host.ShouldBe(expected.Host); - result.Port.ShouldBe(expected.Port); - } - - response.Routes[i].DownstreamPathTemplate.ShouldBe(expecteds.Routes[i].DownstreamPathTemplate); - response.Routes[i].DownstreamScheme.ShouldBe(expecteds.Routes[i].DownstreamScheme); - response.Routes[i].UpstreamPathTemplate.ShouldBe(expecteds.Routes[i].UpstreamPathTemplate); - response.Routes[i].UpstreamHttpMethod.ShouldBe(expecteds.Routes[i].UpstreamHttpMethod); - } - } - - private void GivenIHaveAddedATokenToMyRequest() - { - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - } - - private void GivenIHaveAnOcelotToken(string adminPath) - { - var tokenUrl = $"{adminPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "admin"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "admin"), - new KeyValuePair("grant_type", "client_credentials") - }; - var content = new FormUrlEncodedContent(formData); - - var response = _httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - var configPath = $"{adminPath}/.well-known/openid-configuration"; - response = _httpClient.GetAsync(configPath).Result; - response.EnsureSuccessStatusCode(); - } - - private void GivenOcelotIsRunningWithIdentityServerSettings(Action configOptions) - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .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(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddSingleton(_webHostBuilder); - x.AddOcelot() - .AddAdministration("/administration", configOptions); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenOcelotIsRunning() - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .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(x => - { - x.AddMvc(s => s.EnableEndpointRouting = false); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl) - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .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(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddSingleton(_webHostBuilder); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - var text = File.ReadAllText(configurationPath); - - configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - text = File.ReadAllText(configurationPath); - } - - private void WhenIGetUrlOnTheApiGateway(string url) - { - _response = _httpClient.GetAsync(url).Result; - } - - private void WhenIDeleteOnTheApiGateway(string url) - { - _response = _httpClient.DeleteAsync(url).Result; - } - - private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) - { - _response.StatusCode.ShouldBe(expectedHttpStatusCode); - } - - public void Dispose() - { - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); - _builder?.Dispose(); - _httpClient?.Dispose(); - _identityServerBuilder?.Dispose(); - } - - private void GivenThereIsAFooServiceRunningOn(string baseUrl) - { - _fooServiceBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.UsePathBase("/foo"); - app.Run(async context => - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("foo"); - }); - }); - }).Build(); - - _fooServiceBuilder.Start(); - } - - private void GivenThereIsABarServiceRunningOn(string baseUrl) - { - _barServiceBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.UsePathBase("/bar"); - app.Run(async context => - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("bar"); - }); - }); - }).Build(); - - _barServiceBuilder.Start(); - } - } -} +using IdentityServer4.AccessTokenValidation; +using IdentityServer4.Models; +using IdentityServer4.Test; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using Ocelot.Administration; +using Ocelot.Cache; +using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using Shouldly; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using TestStack.BDDfy; +using Ocelot.Configuration.ChangeTracking; +using Xunit; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; + +namespace Ocelot.IntegrationTests +{ + public class AdministrationTests : IDisposable + { + private HttpClient _httpClient; + private readonly HttpClient _httpClientTwo; + private HttpResponseMessage _response; + private IHost _builder; + private IHostBuilder _webHostBuilder; + private string _ocelotBaseUrl; + private BearerToken _token; + private IHostBuilder _webHostBuilderTwo; + private IHost _builderTwo; + private IHost _identityServerBuilder; + private IHost _fooServiceBuilder; + private IHost _barServiceBuilder; + + public AdministrationTests() + { + _httpClient = new HttpClient(); + _httpClientTwo = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5000"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + } + + [Fact] + public void should_return_response_401_with_call_re_routes_controller() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) + .BDDfy(); + } + + //this seems to be be answer https://github.com/IdentityServer/IdentityServer4/issues/4914 + [Fact] + public void should_return_response_200_with_call_re_routes_controller() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller_using_base_url_added_in_file_config() + { + _httpClient = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5011"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + BaseUrl = _ocelotBaseUrl, + }, + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunningWithNoWebHostBuilder(_ocelotBaseUrl)) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet()) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenAnotherOcelotIsRunning("http://localhost:5017")) + .When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_file_configuration() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + RequestIdKey = "RequestId", + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "127.0.0.1", + }, + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Geoff", + }, + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Dave", + }, + }, + }, + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(configuration)) + .BDDfy(); + } + + [Fact] + public void should_get_file_configuration_edit_and_post_updated_version() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + }, + }, + }; + + var updatedConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/geoffrey", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "123.123.123", + Port = 443, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/blooper/{productId}", + UpstreamHttpMethod = new List { "post" }, + UpstreamPathTemplate = "/test", + }, + }, + }; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration)) + .BDDfy(); + } + + [Fact] + public void should_activate_change_token_when_configuration_is_updated() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration(), + Routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + }, + }, + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", configuration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => TheChangeTokenShouldBeActive()) + .And(x => ThenTheResponseShouldBe(configuration)) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .And(x => ThenTheResponseShouldBe(configuration)) + .And(_ => ThenTheConfigurationIsSavedCorrectly(configuration)) + .BDDfy(); + } + + private void TheChangeTokenShouldBeActive() + { + _builder.Services.GetRequiredService().ChangeToken.HasChanged.ShouldBeTrue(); + } + + 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_route() + { + var fooPort = 47689; + var barPort = 27654; + + var initialConfiguration = new FileConfiguration + { + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = fooPort, + }, + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/foo", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/foo", + }, + }, + }; + + var updatedConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = barPort, + }, + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/bar", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/foo", + }, + }, + }; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}")) + .And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}")) + .And(x => GivenOcelotIsRunning()) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("foo")) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("bar")) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(initialConfiguration)) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("foo")) + .BDDfy(); + } + + [Fact] + public void should_clear_region() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + }, + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + }, + }, + }, + }; + + var regionToClear = "gettest"; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller_when_using_own_identity_server_to_secure_admin_area() + { + var configuration = new FileConfiguration(); + + var identityServerRootUrl = "http://localhost:5123"; + + Action options = o => + { + o.Authority = identityServerRootUrl; + o.RequireHttpsMetadata = false; + o.TokenValidationParameters = new TokenValidationParameters + { + ValidateAudience = false, + }; + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenThereIsAnIdentityServerOn(identityServerRootUrl, "api")) + .And(x => GivenOcelotIsRunningWithIdentityServerSettings(options)) + .And(x => GivenIHaveAToken(identityServerRootUrl)) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + private void GivenIHaveAToken(string url) + { + var formData = new List> + { + new KeyValuePair("client_id", "api"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password"), + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync($"{url}/connect/token", content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName) + { + _identityServerBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiScopes(new List { new ApiScope(apiName) }) + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = apiName, + Enabled = true, + DisplayName = apiName, + Scopes = new List() + { + apiName, + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = apiName, + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List { new Secret("secret".Sha256()) }, + AllowedScopes = new List { apiName }, + AccessTokenType = AccessTokenType.Jwt, + Enabled = true, + }, + }) + .AddTestUsers(new List + { + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "1231231", + }, + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + } + ); + }).Build(); + + _identityServerBuilder.Start(); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; + response.EnsureSuccessStatusCode(); + } + } + + private void GivenAnotherOcelotIsRunning(string baseUrl) + { + _httpClientTwo.BaseAddress = new Uri(baseUrl); + + _webHostBuilderTwo = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .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(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builderTwo = _webHostBuilderTwo.Build(); + + _builderTwo.Start(); + } + + private void GivenIdentityServerSigningEnvironmentalVariablesAreSet() + { + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx"); + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test"); + } + + private void WhenIGetUrlOnTheSecondOcelot(string url) + { + _httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + _response = _httpClientTwo.GetAsync(url).Result; + } + + private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) + { + var json = JsonConvert.SerializeObject(updatedConfiguration); + var content = new StringContent(json); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + _response = _httpClient.PostAsync(url, content).Result; + } + + private void ThenTheResponseShouldBe(List expected) + { + var content = _response.Content.ReadAsStringAsync().Result; + var result = JsonConvert.DeserializeObject(content); + result.Value.ShouldBe(expected); + } + + private void ThenTheResponseBodyShouldBe(string expected) + { + var content = _response.Content.ReadAsStringAsync().Result; + content.ShouldBe(expected); + } + + private void ThenTheResponseShouldBe(FileConfiguration expecteds) + { + var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); + + response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); + response.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); + response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); + response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); + + for (var i = 0; i < response.Routes.Count; i++) + { + for (var j = 0; j < response.Routes[i].DownstreamHostAndPorts.Count; j++) + { + var result = response.Routes[i].DownstreamHostAndPorts[j]; + var expected = expecteds.Routes[i].DownstreamHostAndPorts[j]; + result.Host.ShouldBe(expected.Host); + result.Port.ShouldBe(expected.Port); + } + + response.Routes[i].DownstreamPathTemplate.ShouldBe(expecteds.Routes[i].DownstreamPathTemplate); + response.Routes[i].DownstreamScheme.ShouldBe(expecteds.Routes[i].DownstreamScheme); + response.Routes[i].UpstreamPathTemplate.ShouldBe(expecteds.Routes[i].UpstreamPathTemplate); + response.Routes[i].UpstreamHttpMethod.ShouldBe(expecteds.Routes[i].UpstreamHttpMethod); + } + } + + private void GivenIHaveAddedATokenToMyRequest() + { + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + } + + private void GivenIHaveAnOcelotToken(string adminPath) + { + var tokenUrl = $"{adminPath}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "admin"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "admin"), + new KeyValuePair("grant_type", "client_credentials"), + }; + var content = new FormUrlEncodedContent(formData); + + var response = _httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + var configPath = $"{adminPath}/.well-known/openid-configuration"; + response = _httpClient.GetAsync(configPath).Result; + response.EnsureSuccessStatusCode(); + } + + private void GivenOcelotIsRunningWithIdentityServerSettings(Action configOptions) + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .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(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() + .AddAdministration("/administration", configOptions); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenOcelotIsRunning() + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .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(x => + { + x.AddMvc(s => s.EnableEndpointRouting = false); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl) + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .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(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + var text = File.ReadAllText(configurationPath); + + configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + text = File.ReadAllText(configurationPath); + } + + private void WhenIGetUrlOnTheApiGateway(string url) + { + _response = _httpClient.GetAsync(url).Result; + } + + private void WhenIDeleteOnTheApiGateway(string url) + { + _response = _httpClient.DeleteAsync(url).Result; + } + + private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) + { + _response.StatusCode.ShouldBe(expectedHttpStatusCode); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); + _builder?.Dispose(); + _httpClient?.Dispose(); + _identityServerBuilder?.Dispose(); + } + + private void GivenThereIsAFooServiceRunningOn(string baseUrl) + { + _fooServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase("/foo"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("foo"); + }); + }); + }).Build(); + + _fooServiceBuilder.Start(); + } + + private void GivenThereIsABarServiceRunningOn(string baseUrl) + { + _barServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase("/bar"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("bar"); + }); + }); + }).Build(); + + _barServiceBuilder.Start(); + } + } +} diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 7d862b24f..53a899c19 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -52,7 +52,7 @@ - + diff --git a/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs b/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs index e19025d1d..c4f675cff 100644 --- a/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs +++ b/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs @@ -1,101 +1,102 @@ -namespace Ocelot.UnitTests.Administration -{ +namespace Ocelot.UnitTests.Administration +{ using IdentityServer4.AccessTokenValidation; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Administration; - using Ocelot.DependencyInjection; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Reflection; - using TestStack.BDDfy; - using Xunit; - - public class OcelotAdministrationBuilderTests - { - private readonly IServiceCollection _services; - private IServiceProvider _serviceProvider; - private readonly IConfiguration _configRoot; - private IOcelotBuilder _ocelotBuilder; - private Exception _ex; - - public OcelotAdministrationBuilderTests() - { - _configRoot = new ConfigurationRoot(new List()); - _services = new ServiceCollection(); - _services.AddSingleton(GetHostingEnvironment()); - _services.AddSingleton(_configRoot); - } - - private IWebHostEnvironment GetHostingEnvironment() - { - var environment = new Mock(); - environment - .Setup(e => e.ApplicationName) - .Returns(typeof(OcelotAdministrationBuilderTests).GetTypeInfo().Assembly.GetName().Name); - - return environment.Object; - } - - //keep - [Fact] - public void should_set_up_administration_with_identity_server_options() - { - Action options = o => { }; - - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpAdministration(options)) - .Then(x => ThenAnExceptionIsntThrown()) - .Then(x => ThenTheCorrectAdminPathIsRegitered()) - .BDDfy(); - } - - //keep - [Fact] - public void should_set_up_administration() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenISetUpAdministration()) - .Then(x => ThenAnExceptionIsntThrown()) - .Then(x => ThenTheCorrectAdminPathIsRegitered()) - .BDDfy(); - } - - private void WhenISetUpAdministration() - { - _ocelotBuilder.AddAdministration("/administration", "secret"); - } - - private void WhenISetUpAdministration(Action options) - { - _ocelotBuilder.AddAdministration("/administration", options); - } - - private void ThenTheCorrectAdminPathIsRegitered() - { - _serviceProvider = _services.BuildServiceProvider(); - var path = _serviceProvider.GetService(); - path.Path.ShouldBe("/administration"); - } - - private void WhenISetUpOcelotServices() - { - try - { - _ocelotBuilder = _services.AddOcelot(_configRoot); - } - catch (Exception e) - { - _ex = e; - } - } - - private void ThenAnExceptionIsntThrown() - { - _ex.ShouldBeNull(); - } - } -} + using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Administration; + using Ocelot.DependencyInjection; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Reflection; + using TestStack.BDDfy; + using Xunit; + + public class OcelotAdministrationBuilderTests + { + private readonly IServiceCollection _services; + private IServiceProvider _serviceProvider; + private readonly IConfiguration _configRoot; + private IOcelotBuilder _ocelotBuilder; + private Exception _ex; + + public OcelotAdministrationBuilderTests() + { + _configRoot = new ConfigurationRoot(new List()); + _services = new ServiceCollection(); + _services.AddSingleton(GetHostingEnvironment()); + _services.AddSingleton(_configRoot); + } + + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotAdministrationBuilderTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + + //keep + [Fact] + public void should_set_up_administration_with_identity_server_options() + { + Action options = o => { }; + + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpAdministration(options)) + .Then(x => ThenAnExceptionIsntThrown()) + .Then(x => ThenTheCorrectAdminPathIsRegitered()) + .BDDfy(); + } + + //keep + [Fact] + public void should_set_up_administration() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenISetUpAdministration()) + .Then(x => ThenAnExceptionIsntThrown()) + .Then(x => ThenTheCorrectAdminPathIsRegitered()) + .BDDfy(); + } + + private void WhenISetUpAdministration() + { + _ocelotBuilder.AddAdministration("/administration", "secret"); + } + + private void WhenISetUpAdministration(Action options) + { + _ocelotBuilder.AddAdministration("/administration", options); + } + + private void ThenTheCorrectAdminPathIsRegitered() + { + _serviceProvider = _services.BuildServiceProvider(); + var path = _serviceProvider.GetService(); + path.Path.ShouldBe("/administration"); + } + + private void WhenISetUpOcelotServices() + { + try + { + _ocelotBuilder = _services.AddOcelot(_configRoot); + } + catch (Exception e) + { + _ex = e; + } + } + + private void ThenAnExceptionIsntThrown() + { + _ex.ShouldBeNull(); + } + } +} diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 5b8551e35..959d23315 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -72,7 +72,7 @@ - + From ab3533bb1db524ee745a83552aca4663de099d9e Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 1 Dec 2020 11:08:24 +0000 Subject: [PATCH 30/34] bring back develop --- .circleci/config.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e38eb5ab7..65fe36918 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,6 +27,12 @@ workflows: filters: branches: only: master + develop: + jobs: + - build: + filters: + branches: + only: develop pr: jobs: - build: @@ -34,3 +40,4 @@ workflows: branches: ignore: - master + - develop From ede71c4883883e40f07a260772d10e3556decb48 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 1 Dec 2020 11:14:30 +0000 Subject: [PATCH 31/34] updated release process --- docs/building/releaseprocess.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/building/releaseprocess.rst b/docs/building/releaseprocess.rst index 43cb5f61a..40760652b 100644 --- a/docs/building/releaseprocess.rst +++ b/docs/building/releaseprocess.rst @@ -1,7 +1,7 @@ Release process =============== -* The release process works best with GitHubFlow branching. +* The release process works best with Git Flow branching. * Contributors can do whatever they want on PRs and merges to master will result in packages being released to GitHub and NuGet. Ocelot uses the following process to accept work into the NuGet packages. @@ -10,7 +10,7 @@ Ocelot uses the following process to accept work into the NuGet packages. 2. User creates a fork and branches from this (unless a member of core team, they can just create a branch on the main repo) e.g. feat/xxx, fix/xxx etc. It doesn't really matter what the xxx is. It might make sense to use the issue number and maybe a short description. I don't care as long as it has (feat, fix, refactor)/xxx :) -3. When the user is happy with their work they can create a pull request against master in GitHub with their changes. The user must follow the `SemVer `_ support for this is provided by `GitVersion `_. So if you need to make breaking changes please make sure you use the correct commit message so GitVersion uses the correct semver tags. Do not manually tag the Ocelot repo this will break things. +3. When the user is happy with their work they can create a pull request against develop in GitHub with their changes. The user must follow the `SemVer `_ support for this is provided by `GitVersion `_. So if you need to make breaking changes please make sure you use the correct commit message so GitVersion uses the correct semver tags. Do not manually tag the Ocelot repo this will break things. 4. The Ocelot team will review the PR and if all is good merge it, else they will suggest feedback that the user will need to act on. In order to speed up getting a PR the user should think about the following. - Have I covered all my changes with tests at unit and acceptance level? @@ -23,9 +23,11 @@ In order for a PR to be merged the following must have occured. - Build must not have slowed down dramatically. - The main Ocelot package must not have taken on any non MS dependencies. -5. After the PR is merged to master the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet. +5. After the PR is merged to develop the Ocelot NuGet packages will not be updated until a release is created. -6. The final step is to go back to GitHub and close any issues that are now fixed. You should see something like this in`GitHub `_ and this in `NuGet `_. +6. When enough work has been completed to justify a new release develop will be merged into master the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet. + +7. The final step is to go back to GitHub and close any issues that are now fixed. You should see something like this in`GitHub `_ and this in `NuGet `_. Notes ----- From b2dd70f59c6a9ba9e2504b1a9a3152cbf475d8f9 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 1 Dec 2020 12:15:51 +0000 Subject: [PATCH 32/34] release process note --- docs/building/releaseprocess.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building/releaseprocess.rst b/docs/building/releaseprocess.rst index 40760652b..7a89a1f07 100644 --- a/docs/building/releaseprocess.rst +++ b/docs/building/releaseprocess.rst @@ -25,7 +25,7 @@ In order for a PR to be merged the following must have occured. 5. After the PR is merged to develop the Ocelot NuGet packages will not be updated until a release is created. -6. When enough work has been completed to justify a new release develop will be merged into master the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet. +6. When enough work has been completed to justify a new release. Develop will be merged into master the release process will begin which builds the code, versions it, pushes artifacts to GitHub and NuGet packages to NuGet. 7. The final step is to go back to GitHub and close any issues that are now fixed. You should see something like this in`GitHub `_ and this in `NuGet `_. From b46fedac24ee37c66b127f9888bc80122f9994ba Mon Sep 17 00:00:00 2001 From: TomPallister Date: Tue, 1 Dec 2020 16:54:52 +0000 Subject: [PATCH 33/34] rename authorisation to authorization --- README.md | 2 +- docs/features/authentication.rst | 2 +- .../{authorisation.rst => authorization.rst} | 6 +- docs/features/claimstransformation.rst | 2 +- docs/features/errorcodes.rst | 2 +- docs/features/middlewareinjection.rst | 4 +- docs/features/requestid.rst | 4 +- docs/features/websockets.rst | 2 +- docs/index.rst | 2 +- samples/OcelotGraphQL/README.md | 2 +- .../ClaimValueNotAuthorisedError.cs | 13 --- ...orisationMiddlewareMiddlewareExtensions.cs | 12 --- .../Authorisation/ScopeNotAuthorisedError.cs | 12 --- .../ClaimValueNotAuthorizedError.cs | 13 +++ .../ClaimsAuthorizer.cs} | 24 ++--- .../IClaimsAuthorizer.cs} | 10 +- .../IScopesAuthorizer.cs} | 8 +- .../Middleware/AuthorizationMiddleware.cs} | 66 ++++++------- ...orizationMiddlewareMiddlewareExtensions.cs | 12 +++ .../Authorization/ScopeNotAuthorizedError.cs | 12 +++ .../ScopesAuthorizer.cs} | 94 +++++++++---------- .../UnauthorizedError.cs} | 6 +- .../UserDoesNotHaveClaimError.cs | 8 +- .../Builder/DownstreamReRouteBuilder.cs | 8 +- .../Builder/RouteOptionsBuilder.cs | 8 +- .../Creator/RouteOptionsCreator.cs | 10 +- .../Configuration/Creator/RoutesCreator.cs | 2 +- src/Ocelot/Configuration/DownstreamRoute.cs | 6 +- src/Ocelot/Configuration/RouteOptions.cs | 6 +- .../DependencyInjection/OcelotBuilder.cs | 6 +- src/Ocelot/Errors/OcelotErrorCode.cs | 4 +- .../Middleware/OcelotPipelineConfiguration.cs | 12 +-- .../Middleware/OcelotPipelineExtensions.cs | 16 ++-- .../Responder/ErrorsToHttpStatusCodeMapper.cs | 4 +- ...risationTests.cs => AuthorizationTests.cs} | 8 +- .../CustomMiddlewareTests.cs | 56 +++++------ ...sts.cs => AuthorizationMiddlewareTests.cs} | 30 +++--- ...riserTests.cs => ClaimsAuthorizerTests.cs} | 48 +++++----- .../Configuration/RouteOptionsCreatorTests.cs | 4 +- .../Configuration/RoutesCreatorTests.cs | 2 +- ...ttpHeadersTransformationMiddlewareTests.cs | 4 +- ...riserTests.cs => ScopesAuthorizerTests.cs} | 24 ++--- .../ErrorsToHttpStatusCodeMapperTests.cs | 14 +-- 43 files changed, 295 insertions(+), 295 deletions(-) rename docs/features/{authorisation.rst => authorization.rst} (51%) delete mode 100644 src/Ocelot/Authorisation/ClaimValueNotAuthorisedError.cs delete mode 100644 src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs delete mode 100644 src/Ocelot/Authorisation/ScopeNotAuthorisedError.cs create mode 100644 src/Ocelot/Authorization/ClaimValueNotAuthorizedError.cs rename src/Ocelot/{Authorisation/ClaimsAuthoriser.cs => Authorization/ClaimsAuthorizer.cs} (86%) rename src/Ocelot/{Authorisation/IClaimsAuthoriser.cs => Authorization/IClaimsAuthorizer.cs} (74%) rename src/Ocelot/{Authorisation/IScopesAuthoriser.cs => Authorization/IScopesAuthorizer.cs} (50%) rename src/Ocelot/{Authorisation/Middleware/AuthorisationMiddleware.cs => Authorization/Middleware/AuthorizationMiddleware.cs} (58%) create mode 100644 src/Ocelot/Authorization/Middleware/AuthorizationMiddlewareMiddlewareExtensions.cs create mode 100644 src/Ocelot/Authorization/ScopeNotAuthorizedError.cs rename src/Ocelot/{Authorisation/ScopesAuthoriser.cs => Authorization/ScopesAuthorizer.cs} (78%) rename src/Ocelot/{Authorisation/UnauthorisedError.cs => Authorization/UnauthorizedError.cs} (50%) rename src/Ocelot/{Authorisation => Authorization}/UserDoesNotHaveClaimError.cs (84%) rename test/Ocelot.AcceptanceTests/{AuthorisationTests.cs => AuthorizationTests.cs} (98%) rename test/Ocelot.UnitTests/Authorization/{AuthorisationMiddlewareTests.cs => AuthorizationMiddlewareTests.cs} (78%) rename test/Ocelot.UnitTests/Authorization/{ClaimsAuthoriserTests.cs => ClaimsAuthorizerTests.cs} (75%) rename test/Ocelot.UnitTests/Infrastructure/{ScopesAuthoriserTests.cs => ScopesAuthorizerTests.cs} (87%) diff --git a/README.md b/README.md index a18d15fea..c9ff5a61f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio * Kubernetes * WebSockets * Authentication -* Authorisation +* Authorization * Rate Limiting * Caching * Retry policies / QoS diff --git a/docs/features/authentication.rst b/docs/features/authentication.rst index 8aeca15f4..e41c0033a 100644 --- a/docs/features/authentication.rst +++ b/docs/features/authentication.rst @@ -1,7 +1,7 @@ Authentication ============== -In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorisation or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g. +In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorization or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g. .. code-block:: csharp diff --git a/docs/features/authorisation.rst b/docs/features/authorization.rst similarity index 51% rename from docs/features/authorisation.rst rename to docs/features/authorization.rst index ab1bc1b50..c890c14c0 100644 --- a/docs/features/authorisation.rst +++ b/docs/features/authorization.rst @@ -1,7 +1,7 @@ -Authorisation +Authorization ============= -Ocelot supports claims based authorisation which is run post authentication. This means if you have a route you want to authorise you can add the following to you Route configuration. +Ocelot supports claims based authorization which is run post authentication. This means if you have a route you want to authorize you can add the following to you Route configuration. .. code-block:: json @@ -9,7 +9,7 @@ Ocelot supports claims based authorisation which is run post authentication. Thi "UserType": "registered" } -In this example when the authorisation middleware is called Ocelot will check to seeif the user has the claim type UserType and if the value of that claim is registered. If it isn't then the user will not be authorised and the response will be 403 forbidden. +In this example when the authorization middleware is called Ocelot will check to seeif the user has the claim type UserType and if the value of that claim is registered. If it isn't then the user will not be authorized and the response will be 403 forbidden. diff --git a/docs/features/claimstransformation.rst b/docs/features/claimstransformation.rst index d58801e58..c82657a4c 100644 --- a/docs/features/claimstransformation.rst +++ b/docs/features/claimstransformation.rst @@ -3,7 +3,7 @@ Claims Transformation Ocelot allows the user to access claims and transform them into headers, query string parameters, other claims and change downstream paths. This is only available once a user has been authenticated. -After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorisation middleware is called. After the user is authorised first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware. +After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorization middleware is called. After the user is authorized first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware. The syntax for performing the transforms is the same for each process. In the Route configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate. diff --git a/docs/features/errorcodes.rst b/docs/features/errorcodes.rst index aeea7b633..d07eb3a49 100644 --- a/docs/features/errorcodes.rst +++ b/docs/features/errorcodes.rst @@ -3,7 +3,7 @@ Http Error Status Codes Ocelot will return HTTP status error codes based on internal logic in certain siturations: - 401 if the authentication middleware runs and the user is not authenticated. -- 403 if the authorisation middleware runs and the user is unauthenticated, claim value not authroised, scope not authorised, user doesnt have required claim or cannot find claim. +- 403 if the authorization middleware runs and the user is unauthenticated, claim value not authroised, scope not authorized, user doesnt have required claim or cannot find claim. - 503 if the downstream request times out. - 499 if the request is cancelled by the client. - 404 if unable to find a downstream route. diff --git a/docs/features/middlewareinjection.rst b/docs/features/middlewareinjection.rst index 4b6636582..7fccc599b 100644 --- a/docs/features/middlewareinjection.rst +++ b/docs/features/middlewareinjection.rst @@ -31,9 +31,9 @@ The user can set functions against the following. * AuthenticationMiddleware - This overrides Ocelots authentication middleware. -* PreAuthorisationMiddleware - This allows the user to run pre authorisation logic and then call Ocelot's authorisation middleware. +* PreAuthorizationMiddleware - This allows the user to run pre authorization logic and then call Ocelot's authorization middleware. -* AuthorisationMiddleware - This overrides Ocelots authorisation middleware. +* AuthorizationMiddleware - This overrides Ocelots authorization middleware. * PreQueryStringBuilderMiddleware - This allows the user to manipulate the query string on the http request before it is passed to Ocelots request creator. diff --git a/docs/features/requestid.rst b/docs/features/requestid.rst index 519cc21aa..d36734026 100644 --- a/docs/features/requestid.rst +++ b/docs/features/requestid.rst @@ -44,8 +44,8 @@ Below is an example of the logging when set at Debug level for a normal request. requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.Route.DownstreamPath}, dbug: Ocelot.RateLimit.Middleware.ClientRateLimitMiddleware[0] requestId: asdf, previousRequestId: no previous request id, message: EndpointRateLimiting is not enabled for Ocelot.Values.PathTemplate, - dbug: Ocelot.Authorisation.Middleware.AuthorisationMiddleware[0] - requestId: 1234, previousRequestId: asdf, message: /posts/{postId} route does not require user to be authorised, + dbug: Ocelot.Authorization.Middleware.AuthorizationMiddleware[0] + requestId: 1234, previousRequestId: asdf, message: /posts/{postId} route does not require user to be authorized, dbug: Ocelot.DownstreamUrlCreator.Middleware.DownstreamUrlCreatorMiddleware[0] requestId: 1234, previousRequestId: asdf, message: downstream url is {downstreamUrl.Data.Value}, dbug: Ocelot.Request.Middleware.HttpRequestBuilderMiddleware[0] diff --git a/docs/features/websockets.rst b/docs/features/websockets.rst index 8a1acfdb4..ad185f297 100644 --- a/docs/features/websockets.rst +++ b/docs/features/websockets.rst @@ -102,7 +102,7 @@ Unfortunately a lot of Ocelot's features are non websocket specific such as head 9. Claims Transformation 10. Caching 11. Authentication - If anyone requests it we might be able to do something with basic authentication. -12. Authorisation +12. Authorization I'm not 100% sure what will happen with this feature when it get's into the wild so please make sure you test thoroughly! diff --git a/docs/index.rst b/docs/index.rst index 44ad05c35..983d24715 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,7 +26,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n features/servicefabric features/kubernetes features/authentication - features/authorisation + features/authorization features/websockets features/administration features/ratelimiting diff --git a/samples/OcelotGraphQL/README.md b/samples/OcelotGraphQL/README.md index ed6c16600..7f16ed985 100644 --- a/samples/OcelotGraphQL/README.md +++ b/samples/OcelotGraphQL/README.md @@ -1,7 +1,7 @@ # Ocelot using GraphQL example Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together. -I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorisation / authentication or I would +I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorization / authentication or I would bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that. ## Example diff --git a/src/Ocelot/Authorisation/ClaimValueNotAuthorisedError.cs b/src/Ocelot/Authorisation/ClaimValueNotAuthorisedError.cs deleted file mode 100644 index 98f8309f9..000000000 --- a/src/Ocelot/Authorisation/ClaimValueNotAuthorisedError.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ocelot.Authorisation -{ - using Ocelot.Errors; - using System.Net; - - public class ClaimValueNotAuthorisedError : Error - { - public ClaimValueNotAuthorisedError(string message) - : base(message, OcelotErrorCode.ClaimValueNotAuthorisedError, 403) - { - } - } -} diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs deleted file mode 100644 index 0bd1579a6..000000000 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddlewareMiddlewareExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ocelot.Authorisation.Middleware -{ - using Microsoft.AspNetCore.Builder; - - public static class AuthorisationMiddlewareMiddlewareExtensions - { - public static IApplicationBuilder UseAuthorisationMiddleware(this IApplicationBuilder builder) - { - return builder.UseMiddleware(); - } - } -} diff --git a/src/Ocelot/Authorisation/ScopeNotAuthorisedError.cs b/src/Ocelot/Authorisation/ScopeNotAuthorisedError.cs deleted file mode 100644 index c7f403ded..000000000 --- a/src/Ocelot/Authorisation/ScopeNotAuthorisedError.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ocelot.Authorisation -{ - using Ocelot.Errors; - - public class ScopeNotAuthorisedError : Error - { - public ScopeNotAuthorisedError(string message) - : base(message, OcelotErrorCode.ScopeNotAuthorisedError, 403) - { - } - } -} diff --git a/src/Ocelot/Authorization/ClaimValueNotAuthorizedError.cs b/src/Ocelot/Authorization/ClaimValueNotAuthorizedError.cs new file mode 100644 index 000000000..c4d4e8144 --- /dev/null +++ b/src/Ocelot/Authorization/ClaimValueNotAuthorizedError.cs @@ -0,0 +1,13 @@ +namespace Ocelot.Authorization +{ + using Ocelot.Errors; + using System.Net; + + public class ClaimValueNotAuthorizedError : Error + { + public ClaimValueNotAuthorizedError(string message) + : base(message, OcelotErrorCode.ClaimValueNotAuthorizedError, 403) + { + } + } +} diff --git a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs b/src/Ocelot/Authorization/ClaimsAuthorizer.cs similarity index 86% rename from src/Ocelot/Authorisation/ClaimsAuthoriser.cs rename to src/Ocelot/Authorization/ClaimsAuthorizer.cs index d6c994402..15da73d74 100644 --- a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs +++ b/src/Ocelot/Authorization/ClaimsAuthorizer.cs @@ -1,4 +1,4 @@ -namespace Ocelot.Authorisation +namespace Ocelot.Authorization { using Ocelot.Infrastructure.Claims.Parser; using Ocelot.DownstreamRouteFinder.UrlMatcher; @@ -8,16 +8,16 @@ using System.Security.Claims; using System.Text.RegularExpressions; - public class ClaimsAuthoriser : IClaimsAuthoriser + public class ClaimsAuthorizer : IClaimsAuthorizer { private readonly IClaimsParser _claimsParser; - public ClaimsAuthoriser(IClaimsParser claimsParser) + public ClaimsAuthorizer(IClaimsParser claimsParser) { _claimsParser = claimsParser; } - public Response Authorise( + public Response Authorize( ClaimsPrincipal claimsPrincipal, Dictionary routeClaimsRequirement, List urlPathPlaceholderNameAndValues @@ -45,10 +45,10 @@ List urlPathPlaceholderNameAndValues { // match var actualValue = matchingPlaceholders[0].Value; - var authorised = values.Data.Contains(actualValue); - if (!authorised) + var authorized = values.Data.Contains(actualValue); + if (!authorized) { - return new ErrorResponse(new ClaimValueNotAuthorisedError( + return new ErrorResponse(new ClaimValueNotAuthorizedError( $"dynamic claim value for {variableName} of {string.Join(", ", values.Data)} is not the same as required value: {actualValue}")); } } @@ -57,12 +57,12 @@ List urlPathPlaceholderNameAndValues // config error if (matchingPlaceholders.Length == 0) { - return new ErrorResponse(new ClaimValueNotAuthorisedError( + return new ErrorResponse(new ClaimValueNotAuthorizedError( $"config error: requires variable claim value: {variableName} placeholders does not contain that variable: {string.Join(", ", urlPathPlaceholderNameAndValues.Select(p => p.Name))}")); } else { - return new ErrorResponse(new ClaimValueNotAuthorisedError( + return new ErrorResponse(new ClaimValueNotAuthorizedError( $"config error: requires variable claim value: {required.Value} but placeholders are ambiguous: {string.Join(", ", urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Select(p => p.Value))}")); } } @@ -70,10 +70,10 @@ List urlPathPlaceholderNameAndValues else { // static claim - var authorised = values.Data.Contains(required.Value); - if (!authorised) + var authorized = values.Data.Contains(required.Value); + if (!authorized) { - return new ErrorResponse(new ClaimValueNotAuthorisedError( + return new ErrorResponse(new ClaimValueNotAuthorizedError( $"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}")); } } diff --git a/src/Ocelot/Authorisation/IClaimsAuthoriser.cs b/src/Ocelot/Authorization/IClaimsAuthorizer.cs similarity index 74% rename from src/Ocelot/Authorisation/IClaimsAuthoriser.cs rename to src/Ocelot/Authorization/IClaimsAuthorizer.cs index 5e4b9c59c..dc89f0109 100644 --- a/src/Ocelot/Authorisation/IClaimsAuthoriser.cs +++ b/src/Ocelot/Authorization/IClaimsAuthorizer.cs @@ -2,16 +2,16 @@ using Ocelot.Responses; using System.Security.Claims; -namespace Ocelot.Authorisation +namespace Ocelot.Authorization { using System.Collections.Generic; - public interface IClaimsAuthoriser + public interface IClaimsAuthorizer { - Response Authorise( + Response Authorize( ClaimsPrincipal claimsPrincipal, Dictionary routeClaimsRequirement, - List urlPathPlaceholderNameAndValues + List urlPathPlaceholderNameAndValues ); } -} +} diff --git a/src/Ocelot/Authorisation/IScopesAuthoriser.cs b/src/Ocelot/Authorization/IScopesAuthorizer.cs similarity index 50% rename from src/Ocelot/Authorisation/IScopesAuthoriser.cs rename to src/Ocelot/Authorization/IScopesAuthorizer.cs index 57047ce75..e0041cd28 100644 --- a/src/Ocelot/Authorisation/IScopesAuthoriser.cs +++ b/src/Ocelot/Authorization/IScopesAuthorizer.cs @@ -1,12 +1,12 @@ using Ocelot.Responses; using System.Security.Claims; -namespace Ocelot.Authorisation +namespace Ocelot.Authorization { using System.Collections.Generic; - public interface IScopesAuthoriser + public interface IScopesAuthorizer { - Response Authorise(ClaimsPrincipal claimsPrincipal, List routeAllowedScopes); + Response Authorize(ClaimsPrincipal claimsPrincipal, List routeAllowedScopes); } -} +} diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorization/Middleware/AuthorizationMiddleware.cs similarity index 58% rename from src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs rename to src/Ocelot/Authorization/Middleware/AuthorizationMiddleware.cs index 0d02c62ed..3a9c5d802 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorization/Middleware/AuthorizationMiddleware.cs @@ -1,4 +1,4 @@ -namespace Ocelot.Authorisation.Middleware +namespace Ocelot.Authorization.Middleware { using Ocelot.Configuration; using Ocelot.Logging; @@ -8,21 +8,21 @@ using Microsoft.AspNetCore.Http; using Ocelot.DownstreamRouteFinder.Middleware; - public class AuthorisationMiddleware : OcelotMiddleware + public class AuthorizationMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; - private readonly IClaimsAuthoriser _claimsAuthoriser; - private readonly IScopesAuthoriser _scopesAuthoriser; + private readonly IClaimsAuthorizer _claimsAuthorizer; + private readonly IScopesAuthorizer _scopesAuthorizer; - public AuthorisationMiddleware(RequestDelegate next, - IClaimsAuthoriser claimsAuthoriser, - IScopesAuthoriser scopesAuthoriser, + public AuthorizationMiddleware(RequestDelegate next, + IClaimsAuthorizer claimsAuthorizer, + IScopesAuthorizer scopesAuthorizer, IOcelotLoggerFactory loggerFactory) - : base(loggerFactory.CreateLogger()) + : base(loggerFactory.CreateLogger()) { _next = next; - _claimsAuthoriser = claimsAuthoriser; - _scopesAuthoriser = scopesAuthoriser; + _claimsAuthorizer = claimsAuthorizer; + _scopesAuthorizer = scopesAuthorizer; } public async Task Invoke(HttpContext httpContext) @@ -33,65 +33,65 @@ public async Task Invoke(HttpContext httpContext) { Logger.LogInformation("route is authenticated scopes must be checked"); - var authorised = _scopesAuthoriser.Authorise(httpContext.User, downstreamRoute.AuthenticationOptions.AllowedScopes); + var authorized = _scopesAuthorizer.Authorize(httpContext.User, downstreamRoute.AuthenticationOptions.AllowedScopes); - if (authorised.IsError) + if (authorized.IsError) { - Logger.LogWarning("error authorising user scopes"); + Logger.LogWarning("error authorizing user scopes"); - httpContext.Items.UpsertErrors(authorised.Errors); + httpContext.Items.UpsertErrors(authorized.Errors); return; } - if (IsAuthorised(authorised)) + if (IsAuthorized(authorized)) { - Logger.LogInformation("user scopes is authorised calling next authorisation checks"); + Logger.LogInformation("user scopes is authorized calling next authorization checks"); } else { - Logger.LogWarning("user scopes is not authorised setting pipeline error"); + Logger.LogWarning("user scopes is not authorized setting pipeline error"); - httpContext.Items.SetError(new UnauthorisedError( + httpContext.Items.SetError(new UnauthorizedError( $"{httpContext.User.Identity.Name} unable to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}")); } } - if (!IsOptionsHttpMethod(httpContext) && IsAuthorisedRoute(downstreamRoute)) + if (!IsOptionsHttpMethod(httpContext) && IsAuthorizedRoute(downstreamRoute)) { - Logger.LogInformation("route is authorised"); + Logger.LogInformation("route is authorized"); - var authorised = _claimsAuthoriser.Authorise(httpContext.User, downstreamRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues()); + var authorized = _claimsAuthorizer.Authorize(httpContext.User, downstreamRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues()); - if (authorised.IsError) + if (authorized.IsError) { - Logger.LogWarning($"Error whilst authorising {httpContext.User.Identity.Name}. Setting pipeline error"); + Logger.LogWarning($"Error whilst authorizing {httpContext.User.Identity.Name}. Setting pipeline error"); - httpContext.Items.UpsertErrors(authorised.Errors); + httpContext.Items.UpsertErrors(authorized.Errors); return; } - if (IsAuthorised(authorised)) + if (IsAuthorized(authorized)) { - Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorised for {downstreamRoute.UpstreamPathTemplate.OriginalValue}."); + Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorized for {downstreamRoute.UpstreamPathTemplate.OriginalValue}."); await _next.Invoke(httpContext); } else { - Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorised to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error"); + Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorized to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error"); - httpContext.Items.SetError(new UnauthorisedError($"{httpContext.User.Identity.Name} is not authorised to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}")); + httpContext.Items.SetError(new UnauthorizedError($"{httpContext.User.Identity.Name} is not authorized to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}")); } } else { - Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} route does not require user to be authorised"); + Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} route does not require user to be authorized"); await _next.Invoke(httpContext); } } - private static bool IsAuthorised(Response authorised) + private static bool IsAuthorized(Response authorized) { - return authorised.Data; + return authorized.Data; } private static bool IsAuthenticatedRoute(DownstreamRoute route) @@ -99,9 +99,9 @@ private static bool IsAuthenticatedRoute(DownstreamRoute route) return route.IsAuthenticated; } - private static bool IsAuthorisedRoute(DownstreamRoute route) + private static bool IsAuthorizedRoute(DownstreamRoute route) { - return route.IsAuthorised; + return route.IsAuthorized; } private static bool IsOptionsHttpMethod(HttpContext httpContext) diff --git a/src/Ocelot/Authorization/Middleware/AuthorizationMiddlewareMiddlewareExtensions.cs b/src/Ocelot/Authorization/Middleware/AuthorizationMiddlewareMiddlewareExtensions.cs new file mode 100644 index 000000000..260cc86e0 --- /dev/null +++ b/src/Ocelot/Authorization/Middleware/AuthorizationMiddlewareMiddlewareExtensions.cs @@ -0,0 +1,12 @@ +namespace Ocelot.Authorization.Middleware +{ + using Microsoft.AspNetCore.Builder; + + public static class AuthorizationMiddlewareMiddlewareExtensions + { + public static IApplicationBuilder UseAuthorizationMiddleware(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/src/Ocelot/Authorization/ScopeNotAuthorizedError.cs b/src/Ocelot/Authorization/ScopeNotAuthorizedError.cs new file mode 100644 index 000000000..dc5823a32 --- /dev/null +++ b/src/Ocelot/Authorization/ScopeNotAuthorizedError.cs @@ -0,0 +1,12 @@ +namespace Ocelot.Authorization +{ + using Ocelot.Errors; + + public class ScopeNotAuthorizedError : Error + { + public ScopeNotAuthorizedError(string message) + : base(message, OcelotErrorCode.ScopeNotAuthorizedError, 403) + { + } + } +} diff --git a/src/Ocelot/Authorisation/ScopesAuthoriser.cs b/src/Ocelot/Authorization/ScopesAuthorizer.cs similarity index 78% rename from src/Ocelot/Authorisation/ScopesAuthoriser.cs rename to src/Ocelot/Authorization/ScopesAuthorizer.cs index 8344d80bc..7fd7e2aaf 100644 --- a/src/Ocelot/Authorisation/ScopesAuthoriser.cs +++ b/src/Ocelot/Authorization/ScopesAuthorizer.cs @@ -1,47 +1,47 @@ -using Ocelot.Responses; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; - -namespace Ocelot.Authorisation -{ - using Infrastructure.Claims.Parser; - - public class ScopesAuthoriser : IScopesAuthoriser - { - private readonly IClaimsParser _claimsParser; - private readonly string _scope = "scope"; - - public ScopesAuthoriser(IClaimsParser claimsParser) - { - _claimsParser = claimsParser; - } - - public Response Authorise(ClaimsPrincipal claimsPrincipal, List routeAllowedScopes) - { - if (routeAllowedScopes == null || routeAllowedScopes.Count == 0) - { - return new OkResponse(true); - } - - var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, _scope); - - if (values.IsError) - { - return new ErrorResponse(values.Errors); - } - - var userScopes = values.Data; - - var matchesScopes = routeAllowedScopes.Intersect(userScopes).ToList(); - - if (matchesScopes.Count == 0) - { - return new ErrorResponse( - new ScopeNotAuthorisedError($"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'")); - } - - return new OkResponse(true); - } - } -} +using Ocelot.Responses; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; + +namespace Ocelot.Authorization +{ + using Infrastructure.Claims.Parser; + + public class ScopesAuthorizer : IScopesAuthorizer + { + private readonly IClaimsParser _claimsParser; + private readonly string _scope = "scope"; + + public ScopesAuthorizer(IClaimsParser claimsParser) + { + _claimsParser = claimsParser; + } + + public Response Authorize(ClaimsPrincipal claimsPrincipal, List routeAllowedScopes) + { + if (routeAllowedScopes == null || routeAllowedScopes.Count == 0) + { + return new OkResponse(true); + } + + var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, _scope); + + if (values.IsError) + { + return new ErrorResponse(values.Errors); + } + + var userScopes = values.Data; + + var matchesScopes = routeAllowedScopes.Intersect(userScopes).ToList(); + + if (matchesScopes.Count == 0) + { + return new ErrorResponse( + new ScopeNotAuthorizedError($"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'")); + } + + return new OkResponse(true); + } + } +} diff --git a/src/Ocelot/Authorisation/UnauthorisedError.cs b/src/Ocelot/Authorization/UnauthorizedError.cs similarity index 50% rename from src/Ocelot/Authorisation/UnauthorisedError.cs rename to src/Ocelot/Authorization/UnauthorizedError.cs index fc32ea831..689f9e2f2 100644 --- a/src/Ocelot/Authorisation/UnauthorisedError.cs +++ b/src/Ocelot/Authorization/UnauthorizedError.cs @@ -1,10 +1,10 @@ -namespace Ocelot.Authorisation +namespace Ocelot.Authorization { using Ocelot.Errors; - public class UnauthorisedError : Error + public class UnauthorizedError : Error { - public UnauthorisedError(string message) + public UnauthorizedError(string message) : base(message, OcelotErrorCode.UnauthorizedError, 403) { } diff --git a/src/Ocelot/Authorisation/UserDoesNotHaveClaimError.cs b/src/Ocelot/Authorization/UserDoesNotHaveClaimError.cs similarity index 84% rename from src/Ocelot/Authorisation/UserDoesNotHaveClaimError.cs rename to src/Ocelot/Authorization/UserDoesNotHaveClaimError.cs index 6f9aa3eb2..08c19d9f8 100644 --- a/src/Ocelot/Authorisation/UserDoesNotHaveClaimError.cs +++ b/src/Ocelot/Authorization/UserDoesNotHaveClaimError.cs @@ -1,12 +1,12 @@ -namespace Ocelot.Authorisation -{ +namespace Ocelot.Authorization +{ using Ocelot.Errors; public class UserDoesNotHaveClaimError : Error { - public UserDoesNotHaveClaimError(string message) + public UserDoesNotHaveClaimError(string message) : base(message, OcelotErrorCode.UserDoesNotHaveClaimError, 403) { } } -} +} diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index b51c33db8..5ee5ddf73 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -18,7 +18,7 @@ public class DownstreamRouteBuilder private List _claimsToHeaders; private List _claimToClaims; private Dictionary _routeClaimRequirement; - private bool _isAuthorised; + private bool _isAuthorized; private List _claimToQueries; private List _claimToDownstreamPath; private string _requestIdHeaderKey; @@ -101,9 +101,9 @@ public DownstreamRouteBuilder WithIsAuthenticated(bool input) return this; } - public DownstreamRouteBuilder WithIsAuthorised(bool input) + public DownstreamRouteBuilder WithIsAuthorized(bool input) { - _isAuthorised = input; + _isAuthorized = input; return this; } @@ -289,7 +289,7 @@ public DownstreamRoute Build() _claimToClaims, _claimToDownstreamPath, _isAuthenticated, - _isAuthorised, + _isAuthorized, _authenticationOptions, new DownstreamPathTemplate(_downstreamPathTemplate), _loadBalancerKey, diff --git a/src/Ocelot/Configuration/Builder/RouteOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/RouteOptionsBuilder.cs index ef1cdcd32..7906dad39 100644 --- a/src/Ocelot/Configuration/Builder/RouteOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/RouteOptionsBuilder.cs @@ -3,7 +3,7 @@ namespace Ocelot.Configuration.Builder public class RouteOptionsBuilder { private bool _isAuthenticated; - private bool _isAuthorised; + private bool _isAuthorized; private bool _isCached; private bool _enableRateLimiting; private bool _useServiceDiscovery; @@ -20,9 +20,9 @@ public RouteOptionsBuilder WithIsAuthenticated(bool isAuthenticated) return this; } - public RouteOptionsBuilder WithIsAuthorised(bool isAuthorised) + public RouteOptionsBuilder WithIsAuthorized(bool isAuthorized) { - _isAuthorised = isAuthorised; + _isAuthorized = isAuthorized; return this; } @@ -40,7 +40,7 @@ public RouteOptionsBuilder WithUseServiceDiscovery(bool useServiceDiscovery) public RouteOptions Build() { - return new RouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting, _useServiceDiscovery); + return new RouteOptions(_isAuthenticated, _isAuthorized, _isCached, _enableRateLimiting, _useServiceDiscovery); } } } diff --git a/src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs index 506bd32aa..703cb71f0 100644 --- a/src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs @@ -1,6 +1,6 @@ namespace Ocelot.Configuration.Creator -{ - using Ocelot.Configuration.Builder; +{ + using Ocelot.Configuration.Builder; using Ocelot.Configuration.File; public class RouteOptionsCreator : IRouteOptionsCreator @@ -8,14 +8,14 @@ public class RouteOptionsCreator : IRouteOptionsCreator public RouteOptions Create(FileRoute fileRoute) { var isAuthenticated = IsAuthenticated(fileRoute); - var isAuthorised = IsAuthorised(fileRoute); + var isAuthorized = IsAuthorized(fileRoute); var isCached = IsCached(fileRoute); var enableRateLimiting = IsEnableRateLimiting(fileRoute); var useServiceDiscovery = !string.IsNullOrEmpty(fileRoute.ServiceName); var options = new RouteOptionsBuilder() .WithIsAuthenticated(isAuthenticated) - .WithIsAuthorised(isAuthorised) + .WithIsAuthorized(isAuthorized) .WithIsCached(isCached) .WithRateLimiting(enableRateLimiting) .WithUseServiceDiscovery(useServiceDiscovery) @@ -34,7 +34,7 @@ private bool IsAuthenticated(FileRoute fileRoute) return !string.IsNullOrEmpty(fileRoute.AuthenticationOptions?.AuthenticationProviderKey); } - private bool IsAuthorised(FileRoute fileRoute) + private bool IsAuthorized(FileRoute fileRoute) { return fileRoute.RouteClaimsRequirement?.Count > 0; } diff --git a/src/Ocelot/Configuration/Creator/RoutesCreator.cs b/src/Ocelot/Configuration/Creator/RoutesCreator.cs index a5b348624..48845ce06 100644 --- a/src/Ocelot/Configuration/Creator/RoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/RoutesCreator.cs @@ -119,7 +119,7 @@ private DownstreamRoute SetUpDownstreamRoute(FileRoute fileRoute, FileGlobalConf .WithClaimsToHeaders(claimsToHeaders) .WithClaimsToClaims(claimsToClaims) .WithRouteClaimsRequirement(fileRoute.RouteClaimsRequirement) - .WithIsAuthorised(fileRouteOptions.IsAuthorised) + .WithIsAuthorized(fileRouteOptions.IsAuthorized) .WithClaimsToQueries(claimsToQueries) .WithClaimsToDownstreamPath(claimsToDownstreamPath) .WithRequestIdKey(requestIdKey) diff --git a/src/Ocelot/Configuration/DownstreamRoute.cs b/src/Ocelot/Configuration/DownstreamRoute.cs index 8dd9f30dd..b2bde0ed2 100644 --- a/src/Ocelot/Configuration/DownstreamRoute.cs +++ b/src/Ocelot/Configuration/DownstreamRoute.cs @@ -31,7 +31,7 @@ public DownstreamRoute( List claimsToClaims, List claimsToPath, bool isAuthenticated, - bool isAuthorised, + bool isAuthorized, AuthenticationOptions authenticationOptions, DownstreamPathTemplate downstreamPathTemplate, string loadBalancerKey, @@ -69,7 +69,7 @@ public DownstreamRoute( ClaimsToClaims = claimsToClaims ?? new List(); ClaimsToPath = claimsToPath ?? new List(); IsAuthenticated = isAuthenticated; - IsAuthorised = isAuthorised; + IsAuthorized = isAuthorized; AuthenticationOptions = authenticationOptions; DownstreamPathTemplate = downstreamPathTemplate; LoadBalancerKey = loadBalancerKey; @@ -102,7 +102,7 @@ public DownstreamRoute( public List ClaimsToClaims { get; } public List ClaimsToPath { get; } public bool IsAuthenticated { get; } - public bool IsAuthorised { get; } + public bool IsAuthorized { get; } public AuthenticationOptions AuthenticationOptions { get; } public DownstreamPathTemplate DownstreamPathTemplate { get; } public string LoadBalancerKey { get; } diff --git a/src/Ocelot/Configuration/RouteOptions.cs b/src/Ocelot/Configuration/RouteOptions.cs index 9b5ba8d94..411235989 100644 --- a/src/Ocelot/Configuration/RouteOptions.cs +++ b/src/Ocelot/Configuration/RouteOptions.cs @@ -2,17 +2,17 @@ namespace Ocelot.Configuration { public class RouteOptions { - public RouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery) + public RouteOptions(bool isAuthenticated, bool isAuthorized, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery) { IsAuthenticated = isAuthenticated; - IsAuthorised = isAuthorised; + IsAuthorized = isAuthorized; IsCached = isCached; EnableRateLimiting = isEnableRateLimiting; UseServiceDiscovery = useServiceDiscovery; } public bool IsAuthenticated { get; private set; } - public bool IsAuthorised { get; private set; } + public bool IsAuthorized { get; private set; } public bool IsCached { get; private set; } public bool EnableRateLimiting { get; private set; } public bool UseServiceDiscovery { get; private set; } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index f4fbf8447..20c4e4bb3 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -5,7 +5,7 @@ namespace Ocelot.DependencyInjection using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; - using Ocelot.Authorisation; + using Ocelot.Authorization; using Ocelot.Cache; using Ocelot.Claims; using Ocelot.Configuration; @@ -96,8 +96,8 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index 468641816..9063e7142 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -16,8 +16,8 @@ public enum OcelotErrorCode NoInstructionsError = 11, InstructionNotForClaimsError = 12, UnauthorizedError = 13, - ClaimValueNotAuthorisedError = 14, - ScopeNotAuthorisedError = 15, + ClaimValueNotAuthorizedError = 14, + ScopeNotAuthorizedError = 15, UserDoesNotHaveClaimError = 16, DownstreamPathTemplateContainsSchemeError = 17, DownstreamPathNullOrEmptyError = 18, diff --git a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs index b1f1bf997..f24efa2d6 100644 --- a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs +++ b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs @@ -39,22 +39,22 @@ public class OcelotPipelineConfiguration public Func, Task> AuthenticationMiddleware { get; set; } /// - /// This is to allow the user to run any extra authorisation before the Ocelot authentication + /// This is to allow the user to run any extra authorization before the Ocelot authentication /// kicks in /// /// - /// This is to allow the user to run any extra authorisation before the Ocelot authentication + /// This is to allow the user to run any extra authorization before the Ocelot authentication /// kicks in /// - public Func, Task> PreAuthorisationMiddleware { get; set; } + public Func, Task> PreAuthorizationMiddleware { get; set; } /// - /// This allows the user to completely override the ocelot authorisation middleware + /// This allows the user to completely override the ocelot authorization middleware /// /// - /// This allows the user to completely override the ocelot authorisation middleware + /// This allows the user to completely override the ocelot authorization middleware /// - public Func, Task> AuthorisationMiddleware { get; set; } + public Func, Task> AuthorizationMiddleware { get; set; } /// /// This allows the user to implement there own query string manipulation logic diff --git a/src/Ocelot/Middleware/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/OcelotPipelineExtensions.cs index 6158c72b3..58ab855df 100644 --- a/src/Ocelot/Middleware/OcelotPipelineExtensions.cs +++ b/src/Ocelot/Middleware/OcelotPipelineExtensions.cs @@ -8,7 +8,7 @@ using Ocelot.Responder.Middleware; using Ocelot.Security.Middleware; using Ocelot.Authentication.Middleware; - using Ocelot.Authorisation.Middleware; + using Ocelot.Authorization.Middleware; using Ocelot.Cache.Middleware; using Ocelot.Claims.Middleware; using Ocelot.DownstreamRouteFinder.Middleware; @@ -102,23 +102,23 @@ public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app, app.Use(pipelineConfiguration.AuthenticationMiddleware); } - // The next thing we do is look at any claims transforms in case this is important for authorisation + // The next thing we do is look at any claims transforms in case this is important for authorization app.UseClaimsToClaimsMiddleware(); - // Allow pre authorisation logic. The idea being people might want to run something custom before what is built in. - app.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware); + // Allow pre authorization logic. The idea being people might want to run something custom before what is built in. + app.UseIfNotNull(pipelineConfiguration.PreAuthorizationMiddleware); // Now we have authenticated and done any claims transformation we - // can authorise the request + // can authorize the request // We allow the ocelot middleware to be overriden by whatever the // user wants - if (pipelineConfiguration.AuthorisationMiddleware == null) + if (pipelineConfiguration.AuthorizationMiddleware == null) { - app.UseAuthorisationMiddleware(); + app.UseAuthorizationMiddleware(); } else { - app.Use(pipelineConfiguration.AuthorisationMiddleware); + app.Use(pipelineConfiguration.AuthorizationMiddleware); } // Now we can run the claims to headers transformation middleware diff --git a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs index 39b5e749a..0f74a7adc 100644 --- a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs +++ b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs @@ -14,8 +14,8 @@ public int Map(List errors) } if (errors.Any(e => e.Code == OcelotErrorCode.UnauthorizedError - || e.Code == OcelotErrorCode.ClaimValueNotAuthorisedError - || e.Code == OcelotErrorCode.ScopeNotAuthorisedError + || e.Code == OcelotErrorCode.ClaimValueNotAuthorizedError + || e.Code == OcelotErrorCode.ScopeNotAuthorizedError || e.Code == OcelotErrorCode.UserDoesNotHaveClaimError || e.Code == OcelotErrorCode.CannotFindClaimError)) { diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorizationTests.cs similarity index 98% rename from test/Ocelot.AcceptanceTests/AuthorisationTests.cs rename to test/Ocelot.AcceptanceTests/AuthorizationTests.cs index 9a9a3dd4c..394c76bc2 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorizationTests.cs @@ -16,7 +16,7 @@ namespace Ocelot.AcceptanceTests using TestStack.BDDfy; using Xunit; - public class AuthorisationTests : IDisposable + public class AuthorizationTests : IDisposable { private IWebHost _identityServerBuilder; private readonly Steps _steps; @@ -24,7 +24,7 @@ public class AuthorisationTests : IDisposable private string _identityServerRootUrl; private readonly ServiceHandler _serviceHandler; - public AuthorisationTests() + public AuthorizationTests() { _serviceHandler = new ServiceHandler(); _steps = new Steps(); @@ -41,7 +41,7 @@ public AuthorisationTests() } [Fact] - public void should_return_response_200_authorising_route() + public void should_return_response_200_authorizing_route() { int port = RandomPortFinder.GetRandomPort(); @@ -101,7 +101,7 @@ public void should_return_response_200_authorising_route() } [Fact] - public void should_return_response_403_authorising_route() + public void should_return_response_403_authorizing_route() { int port = RandomPortFinder.GetRandomPort(); diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index f420af6a0..e155010d0 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -10,7 +10,7 @@ using System.Net; using System.Threading.Tasks; using TestStack.BDDfy; - using Xunit; + using Xunit; public class CustomMiddlewareTests : IDisposable { @@ -32,13 +32,13 @@ public void should_call_pre_query_string_builder_middleware() { var configuration = new OcelotPipelineConfiguration { - AuthorisationMiddleware = async (ctx, next) => + AuthorizationMiddleware = async (ctx, next) => { _counter++; await next.Invoke(); } - }; - + }; + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -73,17 +73,17 @@ public void should_call_pre_query_string_builder_middleware() } [Fact] - public void should_call_authorisation_middleware() + public void should_call_authorization_middleware() { var configuration = new OcelotPipelineConfiguration { - AuthorisationMiddleware = async (ctx, next) => + AuthorizationMiddleware = async (ctx, next) => { _counter++; await next.Invoke(); } - }; - + }; + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -127,8 +127,8 @@ public void should_call_authentication_middleware() _counter++; await next.Invoke(); } - }; - + }; + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -172,8 +172,8 @@ public void should_call_pre_error_middleware() _counter++; await next.Invoke(); } - }; - + }; + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -208,17 +208,17 @@ public void should_call_pre_error_middleware() } [Fact] - public void should_call_pre_authorisation_middleware() + public void should_call_pre_authorization_middleware() { var configuration = new OcelotPipelineConfiguration { - PreAuthorisationMiddleware = async (ctx, next) => + PreAuthorizationMiddleware = async (ctx, next) => { _counter++; await next.Invoke(); } - }; - + }; + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -262,8 +262,8 @@ public void should_call_pre_http_authentication_middleware() _counter++; await next.Invoke(); } - }; - + }; + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -295,8 +295,8 @@ public void should_call_pre_http_authentication_middleware() .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => x.ThenTheCounterIs(1)) .BDDfy(); - } - + } + [Fact(Skip = "This is just an example to show how you could hook into Ocelot pipeline with your own middleware. At the moment you must use Response.OnCompleted callback and cannot change the response :( I will see if this can be changed one day!")] public void should_fix_issue_237() { @@ -305,14 +305,14 @@ public void should_fix_issue_237() var httpContext = (HttpContext)state; if (httpContext.Response.StatusCode > 400) - { + { Debug.WriteLine("COUNT CALLED"); Console.WriteLine("COUNT CALLED"); } return Task.CompletedTask; - }; - + }; + var port = RandomPortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -376,8 +376,8 @@ public void Dispose() public class FakeMiddleware { private readonly RequestDelegate _next; - private readonly Func _callback; - + private readonly Func _callback; + public FakeMiddleware(RequestDelegate next, Func callback) { _next = next; @@ -386,10 +386,10 @@ public FakeMiddleware(RequestDelegate next, Func callback) public async Task Invoke(HttpContext context) { - await _next(context); - + await _next(context); + context.Response.OnCompleted(_callback, context); } } } -} +} diff --git a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authorization/AuthorizationMiddlewareTests.cs similarity index 78% rename from test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs rename to test/Ocelot.UnitTests/Authorization/AuthorizationMiddlewareTests.cs index 4c72849e7..f1b14b0ad 100644 --- a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authorization/AuthorizationMiddlewareTests.cs @@ -3,8 +3,8 @@ namespace Ocelot.UnitTests.Authorization { using Microsoft.AspNetCore.Http; using Moq; - using Ocelot.Authorisation; - using Ocelot.Authorisation.Middleware; + using Ocelot.Authorization; + using Ocelot.Authorization.Middleware; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder.Middleware; @@ -18,35 +18,35 @@ namespace Ocelot.UnitTests.Authorization using TestStack.BDDfy; using Xunit; - public class AuthorisationMiddlewareTests + public class AuthorizationMiddlewareTests { - private readonly Mock _authService; - private readonly Mock _authScopesService; + private readonly Mock _authService; + private readonly Mock _authScopesService; private Mock _loggerFactory; private Mock _logger; - private readonly AuthorisationMiddleware _middleware; + private readonly AuthorizationMiddleware _middleware; private RequestDelegate _next; private HttpContext _httpContext; - public AuthorisationMiddlewareTests() + public AuthorizationMiddlewareTests() { _httpContext = new DefaultHttpContext(); - _authService = new Mock(); - _authScopesService = new Mock(); + _authService = new Mock(); + _authScopesService = new Mock(); _loggerFactory = new Mock(); _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _next = context => Task.CompletedTask; - _middleware = new AuthorisationMiddleware(_next, _authService.Object, _authScopesService.Object, _loggerFactory.Object); + _middleware = new AuthorizationMiddleware(_next, _authService.Object, _authScopesService.Object, _loggerFactory.Object); } [Fact] - public void should_call_authorisation_service() + public void should_call_authorization_service() { this.Given(x => x.GivenTheDownStreamRouteIs(new List(), new DownstreamRouteBuilder() .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().Build()) - .WithIsAuthorised(true) + .WithIsAuthorized(true) .WithUpstreamHttpMethod(new List { "Get" }) .Build())) .And(x => x.GivenTheAuthServiceReturns(new OkResponse(true))) @@ -69,7 +69,7 @@ private void GivenTheDownStreamRouteIs(List templatePla private void GivenTheAuthServiceReturns(Response expected) { _authService - .Setup(x => x.Authorise( + .Setup(x => x.Authorize( It.IsAny(), It.IsAny>(), It.IsAny>())) @@ -79,7 +79,7 @@ private void GivenTheAuthServiceReturns(Response expected) private void ThenTheAuthServiceIsCalledCorrectly() { _authService - .Verify(x => x.Authorise( + .Verify(x => x.Authorize( It.IsAny(), It.IsAny>(), It.IsAny>()) diff --git a/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs b/test/Ocelot.UnitTests/Authorization/ClaimsAuthorizerTests.cs similarity index 75% rename from test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs rename to test/Ocelot.UnitTests/Authorization/ClaimsAuthorizerTests.cs index b22e7ffce..34ed4afa5 100644 --- a/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs +++ b/test/Ocelot.UnitTests/Authorization/ClaimsAuthorizerTests.cs @@ -1,4 +1,4 @@ -using Ocelot.Authorisation; +using Ocelot.Authorization; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Responses; using Shouldly; @@ -11,21 +11,21 @@ namespace Ocelot.UnitTests.Authorization { using Ocelot.Infrastructure.Claims.Parser; - public class ClaimsAuthoriserTests + public class ClaimsAuthorizerTests { - private readonly ClaimsAuthoriser _claimsAuthoriser; + private readonly ClaimsAuthorizer _claimsAuthorizer; private ClaimsPrincipal _claimsPrincipal; private Dictionary _requirement; private List _urlPathPlaceholderNameAndValues; private Response _result; - public ClaimsAuthoriserTests() + public ClaimsAuthorizerTests() { - _claimsAuthoriser = new ClaimsAuthoriser(new ClaimsParser()); + _claimsAuthorizer = new ClaimsAuthorizer(new ClaimsParser()); } [Fact] - public void should_authorise_user() + public void should_authorize_user() { this.Given(x => x.GivenAClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new List { @@ -35,8 +35,8 @@ public void should_authorise_user() { {"UserType", "registered"} })) - .When(x => x.WhenICallTheAuthoriser()) - .Then(x => x.ThenTheUserIsAuthorised()) + .When(x => x.WhenICallTheAuthorizer()) + .Then(x => x.ThenTheUserIsAuthorized()) .BDDfy(); } @@ -55,8 +55,8 @@ public void should_authorize_dynamic_user() { new PlaceholderNameAndValue("{userId}", "14") })) - .When(x => x.WhenICallTheAuthoriser()) - .Then(x => x.ThenTheUserIsAuthorised()) + .When(x => x.WhenICallTheAuthorizer()) + .Then(x => x.ThenTheUserIsAuthorized()) .BDDfy(); } @@ -75,13 +75,13 @@ public void should_not_authorize_dynamic_user() { new PlaceholderNameAndValue("{userId}", "14") })) - .When(x => x.WhenICallTheAuthoriser()) - .Then(x => x.ThenTheUserIsntAuthorised()) + .When(x => x.WhenICallTheAuthorizer()) + .Then(x => x.ThenTheUserIsntAuthorized()) .BDDfy(); - } + } [Fact] - public void should_authorise_user_multiple_claims_of_same_type() + public void should_authorize_user_multiple_claims_of_same_type() { this.Given(x => x.GivenAClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new List { @@ -92,21 +92,21 @@ public void should_authorise_user_multiple_claims_of_same_type() { {"UserType", "registered"} })) - .When(x => x.WhenICallTheAuthoriser()) - .Then(x => x.ThenTheUserIsAuthorised()) + .When(x => x.WhenICallTheAuthorizer()) + .Then(x => x.ThenTheUserIsAuthorized()) .BDDfy(); } [Fact] - public void should_not_authorise_user() + public void should_not_authorize_user() { this.Given(x => x.GivenAClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new List())))) .And(x => x.GivenARouteClaimsRequirement(new Dictionary { { "UserType", "registered" } })) - .When(x => x.WhenICallTheAuthoriser()) - .Then(x => x.ThenTheUserIsntAuthorised()) + .When(x => x.WhenICallTheAuthorizer()) + .Then(x => x.ThenTheUserIsntAuthorized()) .BDDfy(); } @@ -125,19 +125,19 @@ private void GivenAPlaceHolderNameAndValueList(List url _urlPathPlaceholderNameAndValues = urlPathPlaceholderNameAndValues; } - private void WhenICallTheAuthoriser() + private void WhenICallTheAuthorizer() { - _result = _claimsAuthoriser.Authorise(_claimsPrincipal, _requirement, _urlPathPlaceholderNameAndValues); + _result = _claimsAuthorizer.Authorize(_claimsPrincipal, _requirement, _urlPathPlaceholderNameAndValues); } - private void ThenTheUserIsAuthorised() + private void ThenTheUserIsAuthorized() { _result.Data.ShouldBe(true); } - private void ThenTheUserIsntAuthorised() + private void ThenTheUserIsntAuthorized() { _result.Data.ShouldBe(false); } } -} +} diff --git a/test/Ocelot.UnitTests/Configuration/RouteOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RouteOptionsCreatorTests.cs index c5e0d75ef..c450abf58 100644 --- a/test/Ocelot.UnitTests/Configuration/RouteOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RouteOptionsCreatorTests.cs @@ -46,7 +46,7 @@ public void should_create_re_route_options() var expected = new RouteOptionsBuilder() .WithIsAuthenticated(true) - .WithIsAuthorised(true) + .WithIsAuthorized(true) .WithIsCached(true) .WithRateLimiting(true) .WithUseServiceDiscovery(true) @@ -71,7 +71,7 @@ private void WhenICreate() private void ThenTheFollowingIsReturned(RouteOptions expected) { _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated); - _result.IsAuthorised.ShouldBe(expected.IsAuthorised); + _result.IsAuthorized.ShouldBe(expected.IsAuthorized); _result.IsCached.ShouldBe(expected.IsCached); _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); _result.UseServiceDiscovery.ShouldBe(expected.UseServiceDiscovery); diff --git a/test/Ocelot.UnitTests/Configuration/RoutesCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RoutesCreatorTests.cs index 81500e51f..1e168cff0 100644 --- a/test/Ocelot.UnitTests/Configuration/RoutesCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RoutesCreatorTests.cs @@ -218,7 +218,7 @@ private void ThenTheRouteIsSet(FileRoute expected, int routeIndex) { _result[routeIndex].DownstreamRoute[0].DownstreamHttpVersion.ShouldBe(_expectedVersion); _result[routeIndex].DownstreamRoute[0].IsAuthenticated.ShouldBe(_rro.IsAuthenticated); - _result[routeIndex].DownstreamRoute[0].IsAuthorised.ShouldBe(_rro.IsAuthorised); + _result[routeIndex].DownstreamRoute[0].IsAuthorized.ShouldBe(_rro.IsAuthorized); _result[routeIndex].DownstreamRoute[0].IsCached.ShouldBe(_rro.IsCached); _result[routeIndex].DownstreamRoute[0].EnableEndpointEndpointRateLimiting.ShouldBe(_rro.EnableRateLimiting); _result[routeIndex].DownstreamRoute[0].RequestIdKey.ShouldBe(_requestId); diff --git a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs index 65aaf0960..788b4cc14 100644 --- a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs @@ -2,7 +2,7 @@ namespace Ocelot.UnitTests.Headers { using Microsoft.AspNetCore.Http; using Moq; - using Ocelot.Authorisation.Middleware; + using Ocelot.Authorization.Middleware; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; @@ -38,7 +38,7 @@ public HttpHeadersTransformationMiddlewareTests() _postReplacer = new Mock(); _loggerFactory = new Mock(); _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _next = context => Task.CompletedTask; _addHeadersToResponse = new Mock(); _addHeadersToRequest = new Mock(); diff --git a/test/Ocelot.UnitTests/Infrastructure/ScopesAuthoriserTests.cs b/test/Ocelot.UnitTests/Infrastructure/ScopesAuthorizerTests.cs similarity index 87% rename from test/Ocelot.UnitTests/Infrastructure/ScopesAuthoriserTests.cs rename to test/Ocelot.UnitTests/Infrastructure/ScopesAuthorizerTests.cs index 08aa78a12..ccc17f195 100644 --- a/test/Ocelot.UnitTests/Infrastructure/ScopesAuthoriserTests.cs +++ b/test/Ocelot.UnitTests/Infrastructure/ScopesAuthorizerTests.cs @@ -1,5 +1,5 @@ using Moq; -using Ocelot.Authorisation; +using Ocelot.Authorization; using Ocelot.Errors; using Ocelot.Infrastructure.Claims.Parser; using Ocelot.Responses; @@ -11,18 +11,18 @@ namespace Ocelot.UnitTests.Infrastructure { - public class ScopesAuthoriserTests + public class ScopesAuthorizerTests { - private ScopesAuthoriser _authoriser; + private ScopesAuthorizer _authorizer; public Mock _parser; private ClaimsPrincipal _principal; private List _allowedScopes; private Response _result; - public ScopesAuthoriserTests() + public ScopesAuthorizerTests() { _parser = new Mock(); - _authoriser = new ScopesAuthoriser(_parser.Object); + _authorizer = new ScopesAuthorizer(_parser.Object); } [Fact] @@ -30,7 +30,7 @@ public void should_return_ok_if_no_allowed_scopes() { this.Given(_ => GivenTheFollowing(new ClaimsPrincipal())) .And(_ => GivenTheFollowing(new List())) - .When(_ => WhenIAuthorise()) + .When(_ => WhenIAuthorize()) .Then(_ => ThenTheFollowingIsReturned(new OkResponse(true))) .BDDfy(); } @@ -40,7 +40,7 @@ public void should_return_ok_if_null_allowed_scopes() { this.Given(_ => GivenTheFollowing(new ClaimsPrincipal())) .And(_ => GivenTheFollowing((List)null)) - .When(_ => WhenIAuthorise()) + .When(_ => WhenIAuthorize()) .Then(_ => ThenTheFollowingIsReturned(new OkResponse(true))) .BDDfy(); } @@ -52,7 +52,7 @@ public void should_return_error_if_claims_parser_returns_error() this.Given(_ => GivenTheFollowing(new ClaimsPrincipal())) .And(_ => GivenTheParserReturns(new ErrorResponse>(fakeError))) .And(_ => GivenTheFollowing(new List() { "doesntmatter" })) - .When(_ => WhenIAuthorise()) + .When(_ => WhenIAuthorize()) .Then(_ => ThenTheFollowingIsReturned(new ErrorResponse(fakeError))) .BDDfy(); } @@ -66,7 +66,7 @@ public void should_match_scopes_and_return_ok_result() this.Given(_ => GivenTheFollowing(claimsPrincipal)) .And(_ => GivenTheParserReturns(new OkResponse>(allowedScopes))) .And(_ => GivenTheFollowing(allowedScopes)) - .When(_ => WhenIAuthorise()) + .When(_ => WhenIAuthorize()) .Then(_ => ThenTheFollowingIsReturned(new OkResponse(true))) .BDDfy(); } @@ -82,7 +82,7 @@ public void should_not_match_scopes_and_return_error_result() this.Given(_ => GivenTheFollowing(claimsPrincipal)) .And(_ => GivenTheParserReturns(new OkResponse>(userScopes))) .And(_ => GivenTheFollowing(allowedScopes)) - .When(_ => WhenIAuthorise()) + .When(_ => WhenIAuthorize()) .Then(_ => ThenTheFollowingIsReturned(new ErrorResponse(fakeError))) .BDDfy(); } @@ -102,9 +102,9 @@ private void GivenTheFollowing(List allowedScopes) _allowedScopes = allowedScopes; } - private void WhenIAuthorise() + private void WhenIAuthorize() { - _result = _authoriser.Authorise(_principal, _allowedScopes); + _result = _authorizer.Authorize(_principal, _allowedScopes); } private void ThenTheFollowingIsReturned(Response expected) diff --git a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs index 04a4496fe..543a1cb92 100644 --- a/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs +++ b/test/Ocelot.UnitTests/Responder/ErrorsToHttpStatusCodeMapperTests.cs @@ -29,10 +29,10 @@ public void should_return_unauthorized(OcelotErrorCode errorCode) [Theory] [InlineData(OcelotErrorCode.CannotFindClaimError)] - [InlineData(OcelotErrorCode.ClaimValueNotAuthorisedError)] - [InlineData(OcelotErrorCode.ScopeNotAuthorisedError)] + [InlineData(OcelotErrorCode.ClaimValueNotAuthorizedError)] + [InlineData(OcelotErrorCode.ScopeNotAuthorizedError)] [InlineData(OcelotErrorCode.UnauthorizedError)] - [InlineData(OcelotErrorCode.UserDoesNotHaveClaimError)] + [InlineData(OcelotErrorCode.UserDoesNotHaveClaimError)] public void should_return_forbidden(OcelotErrorCode errorCode) { ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.Forbidden); @@ -52,8 +52,8 @@ public void should_return_service_unavailable(OcelotErrorCode errorCode) public void should_return_internal_server_error(OcelotErrorCode errorCode) { ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.InternalServerError); - } - + } + [Theory] [InlineData(OcelotErrorCode.ConnectionToDownstreamServiceError)] public void should_return_bad_gateway_error(OcelotErrorCode errorCode) @@ -104,7 +104,7 @@ public void AuthenticationErrorsHaveHighestPriority() } [Fact] - public void AuthorisationErrorsHaveSecondHighestPriority() + public void AuthorizationErrorsHaveSecondHighestPriority() { var errors = new List { @@ -177,4 +177,4 @@ private void ThenTheResponseIsStatusCodeIs(HttpStatusCode expectedCode) _result.ShouldBe((int)expectedCode); } } -} +} From 3ef6abd7465fc77632e4b2d5189fbbf47b457867 Mon Sep 17 00:00:00 2001 From: TomPallister Date: Fri, 11 Dec 2020 10:19:10 +0000 Subject: [PATCH 34/34] +semver: breaking bump to version 17.0.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9ff5a61f..d5b0b8b68 100644 --- a/README.md +++ b/README.md @@ -81,4 +81,4 @@ If you think this project is worth supporting financially please make a contribu ## 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) +[![](https://codescene.io/projects/697/status.svg) Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results) \ No newline at end of file