Merge remote-tracking branch 'upstream/release-3.1.8' into feature/AddStyleCopAnalyzers

# Conflicts:
#	src/Ocelot/Configuration/ServiceProviderConfiguration.cs
This commit is contained in:
Philip Wood 2018-03-03 16:28:19 +00:00
commit 52166b4987
70 changed files with 2373 additions and 481 deletions

View File

@ -6,8 +6,10 @@ for the downstream service Ocelot is forwarding a request to. At the moment this
GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes
you specify a ServiceName for at ReRoute level.
At the moment the only supported service discovery provider is Consul. The following is required in the
GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
Consul
^^^^^^
The following is required in the GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
will be used.
.. code-block:: json

View File

@ -0,0 +1,35 @@
Service Fabric
==============
If you have services deployed in Service Fabric you will normally use the naming service to access them.
The following example shows how to set up a ReRoute that will work in Service Fabric. The most important thing is the ServiceName which is made up of the
Service Fabric application name then the specific service name. We also need to set UseServiceDiscovery as true and set up the ServiceDiscoveryProvider in
GlobalConfiguration. The example here shows a typical configuration. It assumes service fabric is running on localhost and that the naming service is on port 19081.
The example below is taken from the samples folder so please check it if this doesnt make sense!
.. code-block:: json
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/values",
"UpstreamPathTemplate": "/EquipmentInterfaces",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"ServiceName": "OcelotServiceApplication/OcelotApplicationService",
"UseServiceDiscovery" : true
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 19081,
"Type": "ServiceFabric"
}
}
}

View File

@ -22,6 +22,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/routing
features/requestaggregation
features/servicediscovery
features/servicefabric
features/authentication
features/authorisation
features/administration

269
samples/OcelotServiceFabric/.gitignore vendored Normal file
View File

@ -0,0 +1,269 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Service fabric
OcelotApplicationApiGatewayPkg/Code
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/appsettings.json
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/configuration.json
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/runtimes/
OcelotApplicationServicePkg/Code
OcelotApplication/OcelotApplicationApiGatewayPkg/Code/web.config
OcelotApplication/OcelotApplicationServicePkg/Code/runtimes/
!entryPoint.cmd
!entryPoint.sh
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# Dotnet generated files
*.dll
*.pdb
*.deps.json
*.runtimeconfig.json

View File

@ -0,0 +1,11 @@
# Contributing to Azure samples
Thank you for your interest in contributing to Azure samples!
## Ways to contribute
You can contribute to [Azure samples](https://azure.microsoft.com/documentation/samples/) in a few different ways:
- Submit feedback on [this sample page](https://azure.microsoft.com/documentation/samples/service-fabric-dotnet-web-reference-app/) whether it was helpful or not.
- Submit issues through [issue tracker](https://github.com/Azure-Samples/service-fabric-dotnet-web-reference-app/issues) on GitHub. We are actively monitoring the issues and improving our samples.
- If you wish to make code changes to samples, or contribute something new, please follow the [GitHub Forks / Pull requests model](https://help.github.com/articles/fork-a-repo/): Fork the sample repo, make the change and propose it back by submitting a pull request.

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Azure Samples
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="OcelotServiceApplicationType"
ApplicationTypeVersion="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Parameters>
<Parameter Name="OcelotApplicationService_InstanceCount" DefaultValue="1" />
<Parameter Name="OcelotApplicationApiGateway_InstanceCount" DefaultValue="1" />
</Parameters>
<!-- Import the ServiceManifest from the ServicePackage. The ServiceManifestName and ServiceManifestVersion
should match the Name and Version attributes of the ServiceManifest element defined in the
ServiceManifest.xml file. -->
<ServiceManifestImport>
<ServiceManifestRef ServiceManifestName="OcelotApplicationServicePkg" ServiceManifestVersion="1.0.0" />
<ConfigOverrides />
</ServiceManifestImport>
<ServiceManifestImport>
<ServiceManifestRef ServiceManifestName="OcelotApplicationApiGatewayPkg" ServiceManifestVersion="1.0.0" />
<ConfigOverrides />
</ServiceManifestImport>
<DefaultServices>
<!-- The section below creates instances of service types, when an instance of this
application type is created. You can also create one or more instances of service type using the
ServiceFabric PowerShell module.
The attribute ServiceTypeName below must match the name defined in the imported ServiceManifest.xml file. -->
<Service Name="OcelotApplicationService">
<StatelessService ServiceTypeName="OcelotApplicationServiceType" InstanceCount="[OcelotApplicationService_InstanceCount]">
<SingletonPartition />
</StatelessService>
</Service>
<Service Name="OcelotApplicationApiGateway">
<StatelessService ServiceTypeName="OcelotApplicationApiGatewayType" InstanceCount="[OcelotApplicationApiGateway_InstanceCount]">
<SingletonPartition />
</StatelessService>
</Service>
</DefaultServices>
</ApplicationManifest>

View File

@ -0,0 +1,2 @@
dotnet %~dp0\OcelotApplicationApiGateway.dll
exit /b %errorlevel%

View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
check_errs()
{
# Function. Parameter 1 is the return code
if [ "${1}" -ne "0" ]; then
# make our script exit with the right error code.
exit ${1}
fi
}
DIR=`dirname $0`
echo 0x3f > /proc/self/coredump_filter
source $DIR/dotnet-include.sh
dotnet $DIR/OcelotApplicationApiGateway.dll $@
check_errs $?

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<!-- Add your custom configuration sections and parameters here -->
<!--
<Section Name="MyConfigSection">
<Parameter Name="MyParameter" Value="Value1" />
</Section>
-->
</Settings>

View File

@ -0,0 +1,12 @@
contains a Settings.xml file, that can specify parameters for the service
Configuration packages describe user-defined, application-overridable configuration settings (sections of key-value pairs)
required for running service replicas/instances of service types specified in the ser-vice manifest. The configuration settings
must be stored in Settings.xml in the config package folder.
The service developer uses Service Fabric APIs to locate the package folders and read applica-tion-overridable configuration settings.
The service developer can also register callbacks that are in-voked when any of the configuration packages specified in the
service manifest are upgraded and re-reads new configuration settings inside that callback.
This means that Service Fabric will not recycle EXEs and DLLHOSTs specified in the host and support packages when
configuration packages are up-graded.

View File

@ -0,0 +1,5 @@
Data packages contain data files like custom dictionaries,
non-overridable configuration files, custom initialized data files, etc.
Service Fabric will recycle all EXEs and DLLHOSTs specified in the host and support packages when any of the data packages
specified inside service manifest are upgraded.

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="OcelotApplicationApiGatewayPkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="OcelotApplicationApiGatewayType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>entryPoint.sh</Program>
<ConsoleRedirection FileRetentionCount="5" FileMaxSizeInKb="2048"/>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Name="WebEndpoint" Protocol="http" Port="31002" />
</Endpoints>
</Resources>
</ServiceManifest>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="OcelotApplicationApiGatewayPkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="OcelotApplicationApiGatewayType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>entryPoint.cmd</Program>
<ConsoleRedirection FileRetentionCount="5" FileMaxSizeInKb="2048"/>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Name="WebEndpoint" Protocol="http" Port="31002" />
</Endpoints>
</Resources>
</ServiceManifest>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="OcelotApplicationApiGatewayPkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="OcelotApplicationApiGatewayType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>entryPoint.cmd</Program>
<ConsoleRedirection FileRetentionCount="5" FileMaxSizeInKb="2048"/>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Name="WebEndpoint" Protocol="http" Port="31002" />
</Endpoints>
</Resources>
</ServiceManifest>

View File

@ -0,0 +1,2 @@
dotnet %~dp0\OcelotApplicationService.dll
exit /b %errorlevel%

View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
check_errs()
{
# Function. Parameter 1 is the return code
if [ "${1}" -ne "0" ]; then
# make our script exit with the right error code.
exit ${1}
fi
}
DIR=`dirname $0`
source $DIR/dotnet-include.sh
dotnet $DIR/OcelotApplicationService.dll $@
check_errs $?

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<!-- Add your custom configuration sections and parameters here -->
<!--
<Section Name="MyConfigSection">
<Parameter Name="MyParameter" Value="Value1" />
</Section>
-->
</Settings>

View File

@ -0,0 +1,12 @@
contains a Settings.xml file, that can specify parameters for the service
Configuration packages describe user-defined, application-overridable configuration settings (sections of key-value pairs)
required for running service replicas/instances of service types specified in the ser-vice manifest. The configuration settings
must be stored in Settings.xml in the config package folder.
The service developer uses Service Fabric APIs to locate the package folders and read applica-tion-overridable configuration settings.
The service developer can also register callbacks that are in-voked when any of the configuration packages specified in the
service manifest are upgraded and re-reads new configuration settings inside that callback.
This means that Service Fabric will not recycle EXEs and DLLHOSTs specified in the host and support packages when
configuration packages are up-graded.

View File

@ -0,0 +1,5 @@
Data packages contain data files like custom dictionaries,
non-overridable configuration files, custom initialized data files, etc.
Service Fabric will recycle all EXEs and DLLHOSTs specified in the host and support packages when any of the data packages
specified inside service manifest are upgraded.

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="OcelotApplicationServicePkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="OcelotApplicationServiceType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>entryPoint.sh</Program>
<ConsoleRedirection FileRetentionCount="5" FileMaxSizeInKb="2048"/>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpoint" Protocol="http"/>
</Endpoints>
</Resources>
</ServiceManifest>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="OcelotApplicationServicePkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="OcelotApplicationServiceType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>entryPoint.cmd</Program>
<ConsoleRedirection FileRetentionCount="5" FileMaxSizeInKb="2048"/>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpoint" Protocol="http"/>
</Endpoints>
</Resources>
</ServiceManifest>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="OcelotApplicationServicePkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="OcelotApplicationServiceType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>entryPoint.cmd</Program>
<ConsoleRedirection FileRetentionCount="5" FileMaxSizeInKb="2048"/>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpoint" Protocol="http"/>
</Endpoints>
</Resources>
</ServiceManifest>

View File

@ -0,0 +1,51 @@
---
services: service-fabric
platforms: dotnet
author: raunakpandya edited by Tom Pallister for Ocelot
---
# Ocelot Service Fabric example
This shows a service fabric cluster with Ocelot exposed over HTTP accessing services in the cluster via the naming service. If you want to try and use Ocelot with
Service Fabric I reccomend using this as a starting point.
Ocelot does not support partitioned services (stateful & actors) at the moment.
I have not tested this sample on Service Fabric hosted on Linux just a Windows dev cluster. This sample assumes a good understanding of Service Fabric.
The rest of this document is from the Microsoft asp.net core service fabric getting started guide.
# Getting started with Service Fabric with .NET Core
This repository contains a set of simple sample projects to help you getting started with Service Fabric on Linux with .NET Core as the framework. As a pre requisite ensure you have the Service Fabric C# SDK installed on ubuntu box. Follow these instruction to [prepare your development environment on Linux][service-fabric-Linux-getting-started]
### Folder Hierarchy
* src/ - Source of the application divided by different modules by sub-folders.
* &lt;application package folder&gt;/ - Service Fabric Application folder heirarchy. After compilation the executables are placed in code subfolders.
* build.sh - Script to build source on Linux shell.
* build.ps1 - PowerShell script to build source on Windows.
* install.sh - Script to install Application from Linux shell.
* install.ps1 - PowerShell script to install application from Windows. Before calling this script run Connect-ServiceFabricCluster localhost:19000 or however you prefer to connect.
* uninstall.sh - Script to uninstall application from Linux shell.
* uninstall.ps1 - PowerShell script to unintall application from Windows.
* dotnet-include.sh - Script to conditionally handle RHEL dotnet cli through scl(software collections)
# Testing
Once everything is up and running on your dev cluster visit http://localhost:31002/EquipmentInterfaces and you should see the following returned.
```json
["value1","value2"]
```
If you get any errors please check the service fabric logs and let me know if you need help.
## More information
The [Service Fabric documentation][service-fabric-docs] includes a rich set of tutorials and conceptual articles, which serve as a good complement to the samples.
<!-- Links -->
[service-fabric-programming-models]: https://azure.microsoft.com/en-us/documentation/articles/service-fabric-choose-framework/
[service-fabric-docs]: http://aka.ms/servicefabricdocs
[service-fabric-Linux-getting-started]: https://azure.microsoft.com/en-us/documentation/articles/service-fabric-get-started-linux/

View File

@ -0,0 +1,12 @@
cd ./src/OcelotApplicationService/
dotnet restore -s https://api.nuget.org/v3/index.json
dotnet build
dotnet publish -o ../../OcelotApplication/OcelotApplicationServicePkg/Code
cd ../../
cd ./src/OcelotApplicationApiGateway/
dotnet restore -s https://api.nuget.org/v3/index.json
dotnet build
dotnet publish -o ../../OcelotApplication/OcelotApplicationApiGatewayPkg/Code
cd ../../

View File

@ -0,0 +1,15 @@
#!/bin/bash
DIR=`dirname $0`
source $DIR/dotnet-include.sh
cd $DIR/src/OcelotApplicationService/
dotnet restore -s https://api.nuget.org/v3/index.json
dotnet build
dotnet publish -o ../../OcelotApplication/OcelotApplicationServicePkg/Code
cd -
cd $DIR/src/OcelotApplicationApiGateway/
dotnet restore -s https://api.nuget.org/v3/index.json
dotnet build
dotnet publish -o ../../OcelotApplication/OcelotApplicationApiGatewayPkg/Code
cd -

View File

@ -0,0 +1,12 @@
#!/bin/bash
. /etc/os-release
linuxDistrib=$ID
if [ $linuxDistrib = "rhel" ]; then
source scl_source enable rh-dotnet20
exitCode=$?
if [ $exitCode != 0 ]; then
echo "Failed: source scl_source enable rh-dotnet20 : ExitCode: $exitCode"
exit $exitCode
fi
fi

View File

@ -0,0 +1,20 @@
$AppPath = "$PSScriptRoot\OcelotApplication"
$sdkInstallPath = (Get-ItemProperty 'HKLM:\Software\Microsoft\Service Fabric SDK').FabricSDKInstallPath
$sfSdkPsModulePath = $sdkInstallPath + "Tools\PSModule\ServiceFabricSDK"
Import-Module $sfSdkPsModulePath\ServiceFabricSDK.psm1
$StatefulServiceManifestlocation = $AppPath + "\OcelotApplicationServicePkg\"
$StatefulServiceManifestlocationLinux = $StatefulServiceManifestlocation + "\ServiceManifest-Linux.xml"
$StatefulServiceManifestlocationWindows = $StatefulServiceManifestlocation + "\ServiceManifest-Windows.xml"
$StatefulServiceManifestlocationFinal= $StatefulServiceManifestlocation + "ServiceManifest.xml"
Copy-Item -Path $StatefulServiceManifestlocationWindows -Destination $StatefulServiceManifestlocationFinal -Force
$WebServiceManifestlocation = $AppPath + "\OcelotApplicationApiGatewayPkg\"
$WebServiceManifestlocationLinux = $WebServiceManifestlocation + "\ServiceManifest-Linux.xml"
$WebServiceManifestlocationWindows = $WebServiceManifestlocation + "\ServiceManifest-Windows.xml"
$WebServiceManifestlocationFinal= $WebServiceManifestlocation + "ServiceManifest.xml"
Copy-Item -Path $WebServiceManifestlocationWindows -Destination $WebServiceManifestlocationFinal -Force
Copy-ServiceFabricApplicationPackage -ApplicationPackagePath $AppPath -ApplicationPackagePathInImageStore OcelotServiceApplicationType -ImageStoreConnectionString (Get-ImageStoreConnectionStringFromClusterManifest(Get-ServiceFabricClusterManifest)) -TimeoutSec 1800
Register-ServiceFabricApplicationType OcelotServiceApplicationType
New-ServiceFabricApplication fabric:/OcelotServiceApplication OcelotServiceApplicationType 1.0.0

View File

@ -0,0 +1,22 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
appPkg="$DIR/OcelotServiceApplication"
WebServiceManifestlocation="$appPkg/OcelotApplicationApiGatewayPkg"
WebServiceManifestlocationLinux="$WebServiceManifestlocation/ServiceManifest-Linux.xml"
WebServiceManifestlocationWindows="$WebServiceManifestlocation/ServiceManifest-Windows.xml"
WebServiceManifestlocation="$WebServiceManifestlocation/ServiceManifest.xml"
cp $WebServiceManifestlocationLinux $WebServiceManifestlocation
StatefulServiceManifestlocation="$appPkg/OcelotApplicationServicePkg"
StatefulServiceManifestlocationLinux="$StatefulServiceManifestlocation/ServiceManifest-Linux.xml"
StatefulServiceManifestlocationWindows="$StatefulServiceManifestlocation/ServiceManifest-Windows.xml"
StatefulServiceManifestlocation="$StatefulServiceManifestlocation/ServiceManifest.xml"
cp $StatefulServiceManifestlocationLinux $StatefulServiceManifestlocation
cp dotnet-include.sh ./OcelotServiceApplication/OcelotApplicationServicePkg/Code
cp dotnet-include.sh ./OcelotServiceApplication/OcelotApplicationApiGatewayPkg/Code
sfctl application upload --path OcelotServiceApplication --show-progress
sfctl application provision --application-type-build-path OcelotServiceApplication
sfctl application create --app-name fabric:/OcelotServiceApplication --app-type OcelotServiceApplicationType --app-version 1.0.0

View File

@ -0,0 +1,31 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
namespace OcelotApplicationApiGateway
{
using System.Fabric;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
using System.Collections.Generic;
/// Service that handles front-end web requests and acts as a proxy to the back-end data for the UI web page.
/// It is a stateless service that hosts a Web API application on OWIN.
internal sealed class OcelotServiceWebService : StatelessService
{
public OcelotServiceWebService(StatelessServiceContext context)
: base(context)
{ }
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(
initparams => new WebCommunicationListener(string.Empty, initparams),
"OcelotServiceWebListener")
};
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Description>Stateless Web Service for Stateful OcelotApplicationApiGateway App</Description>
<Authors> </Authors>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>OcelotApplicationApiGateway</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>OcelotApplicationApiGateway</PackageId>
</PropertyGroup>
<ItemGroup>
<None Update="configuration.json;appsettings.json;">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456" />
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,51 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
namespace OcelotApplicationApiGateway
{
using System;
using System.Fabric;
using System.Threading;
using Microsoft.ServiceFabric.Services.Runtime;
using System.Diagnostics.Tracing;
/// <summary>
/// The service host is the executable that hosts the Service instances.
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// Create Service Fabric runtime and register the service type.
try
{
//Creating a new event listener to redirect the traces to a file
ServiceEventListener listener = new ServiceEventListener("OcelotApplicationApiGateway");
listener.EnableEvents(ServiceEventSource.Current, EventLevel.LogAlways, EventKeywords.All);
// The ServiceManifest.XML file defines one or more service type names.
// Registering a service maps a service type name to a .NET type.
// When Service Fabric creates an instance of this service type,
// an instance of the class is created in this host process.
ServiceRuntime
.RegisterServiceAsync("OcelotApplicationApiGatewayType", context => new OcelotServiceWebService (context))
.GetAwaiter()
.GetResult();
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
}
catch (Exception ex)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(ex);
throw ex;
}
}
}
}

View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:36034/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"OcelotApplicationApiGateway": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:36035/"
}
}
}

View File

@ -0,0 +1,94 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
namespace OcelotApplicationApiGateway
{
using System;
using System.IO;
using System.Linq;
using System.Diagnostics.Tracing;
using System.Fabric;
using System.Fabric.Common;
using System.Fabric.Common.Tracing;
using Microsoft.ServiceFabric.Services.Runtime;
using System.Globalization;
using System.Text;
/// <summary>
/// ServiceEventListener is a class which listens to the eventsources registered and redirects the traces to a file
/// Note that this class serves as a template to EventListener class and redirects the logs to /tmp/{appnameyyyyMMddHHmmssffff}.
/// You can extend the functionality by writing your code to implement rolling logs for the logs written through this class.
/// You can also write your custom listener class and handle the registered evestsources accordingly.
/// </summary>
internal class ServiceEventListener : EventListener
{
private string fileName;
private string filepath = Path.GetTempPath();
public ServiceEventListener(string appName)
{
this.fileName = appName + DateTime.Now.ToString("yyyyMMddHHmmssffff");
}
/// <summary>
/// We override this method to get a callback on every event we subscribed to with EnableEvents
/// </summary>
/// <param name="eventData">The event arguments that describe the event.</param>
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
using (StreamWriter writer = new StreamWriter( new FileStream(filepath + fileName, FileMode.Append)))
{
// report all event information
writer.Write(" {0} ", Write(eventData.Task.ToString(),
eventData.EventName,
eventData.EventId.ToString(),
eventData.Level));
if (eventData.Message != null)
{
writer.WriteLine(string.Format(CultureInfo.InvariantCulture, eventData.Message, eventData.Payload.ToArray()));
}
}
}
private static String Write(string taskName, string eventName, string id, EventLevel level)
{
StringBuilder output = new StringBuilder();
DateTime now = DateTime.UtcNow;
output.Append(now.ToString("yyyy/MM/dd-HH:mm:ss.fff", CultureInfo.InvariantCulture));
output.Append(',');
output.Append(ConvertLevelToString(level));
output.Append(',');
output.Append(taskName);
if (!string.IsNullOrEmpty(eventName))
{
output.Append('.');
output.Append(eventName);
}
if (!string.IsNullOrEmpty(id))
{
output.Append('@');
output.Append(id);
}
output.Append(',');
return output.ToString();
}
private static string ConvertLevelToString(EventLevel level)
{
switch (level)
{
case EventLevel.Informational:
return "Info";
default:
return level.ToString();
}
}
}
}

View File

@ -0,0 +1,86 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
namespace OcelotApplicationApiGateway
{
using System;
using System.Diagnostics.Tracing;
using System.Fabric;
using Microsoft.ServiceFabric.Services.Runtime;
/// <summary>
/// Implements methods for logging service related events.
/// </summary>
public class ServiceEventSource : EventSource
{
public static ServiceEventSource Current = new ServiceEventSource();
// Define an instance method for each event you want to record and apply an [Event] attribute to it.
// The method name is the name of the event.
// Pass any parameters you want to record with the event (only primitive integer types, DateTime, Guid & string are allowed).
// Each event method implementation should check whether the event source is enabled, and if it is, call WriteEvent() method to raise the event.
// The number and types of arguments passed to every event method must exactly match what is passed to WriteEvent().
// Put [NonEvent] attribute on all methods that do not define an event.
// For more information see https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource.aspx
[NonEvent]
public void Message(string message, params object[] args)
{
if (this.IsEnabled())
{
var finalMessage = string.Format(message, args);
this.Message(finalMessage);
}
}
private const int MessageEventId = 1;
[Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")]
public void Message(string message)
{
if (this.IsEnabled())
{
this.WriteEvent(MessageEventId, message);
}
}
private const int ServiceTypeRegisteredEventId = 3;
[Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}")]
public void ServiceTypeRegistered(int hostProcessId, string serviceType)
{
this.WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
}
[NonEvent]
public void ServiceHostInitializationFailed(Exception e)
{
this.ServiceHostInitializationFailed(e.ToString());
}
private const int ServiceHostInitializationFailedEventId = 4;
[Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed: {0}")]
private void ServiceHostInitializationFailed(string exception)
{
this.WriteEvent(ServiceHostInitializationFailedEventId, exception);
}
[NonEvent]
public void ServiceWebHostBuilderFailed(Exception e)
{
this.ServiceWebHostBuilderFailed(e.ToString());
}
private const int ServiceWebHostBuilderFailedEventId = 5;
[Event(ServiceWebHostBuilderFailedEventId, Level = EventLevel.Error, Message = "Service Owin Web Host Builder Failed: {0}")]
private void ServiceWebHostBuilderFailed(string exception)
{
this.WriteEvent(ServiceWebHostBuilderFailedEventId, exception);
}
}
}

View File

@ -0,0 +1,125 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
namespace OcelotApplicationApiGateway
{
using System;
using System.Fabric;
using System.Fabric.Description;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
public class WebCommunicationListener : ICommunicationListener
{
private readonly string appRoot;
private readonly ServiceContext serviceInitializationParameters;
private string listeningAddress;
private string publishAddress;
// OWIN server handle.
private IWebHost webHost;
public WebCommunicationListener(string appRoot, ServiceContext serviceInitializationParameters)
{
this.appRoot = appRoot;
this.serviceInitializationParameters = serviceInitializationParameters;
}
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
ServiceEventSource.Current.Message("Initialize");
EndpointResourceDescription serviceEndpoint = this.serviceInitializationParameters.CodePackageActivationContext.GetEndpoint("WebEndpoint");
int port = serviceEndpoint.Port;
this.listeningAddress = string.Format(
CultureInfo.InvariantCulture,
"http://+:{0}/{1}",
port,
string.IsNullOrWhiteSpace(this.appRoot)
? string.Empty
: this.appRoot.TrimEnd('/') + '/');
this.publishAddress = this.listeningAddress.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
ServiceEventSource.Current.Message("Starting web server on {0}", this.listeningAddress);
try
{
this.webHost = new WebHostBuilder()
.UseKestrel()
//.UseStartup<Startup>()
.UseUrls(this.listeningAddress)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.ConfigureServices(s => {
s.AddOcelot();
})
.Configure(a => {
a.UseOcelot().Wait();
})
.Build();
this.webHost.Start();
}
catch (Exception ex)
{
ServiceEventSource.Current.ServiceWebHostBuilderFailed(ex);
}
return Task.FromResult(this.publishAddress);
}
public Task CloseAsync(CancellationToken cancellationToken)
{
this.StopAll();
return Task.FromResult(true);
}
public void Abort()
{
this.StopAll();
}
/// <summary>
/// Stops, cancels, and disposes everything.
/// </summary>
private void StopAll()
{
try
{
if (this.webHost != null)
{
ServiceEventSource.Current.Message("Stopping web server.");
this.webHost.Dispose();
}
}
catch (ObjectDisposedException)
{
}
}
}
}

View File

@ -0,0 +1,10 @@
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"System": "Information",
"Microsoft": "Information"
}
}
}

View File

@ -0,0 +1,22 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/values",
"UpstreamPathTemplate": "/EquipmentInterfaces",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"ServiceName": "OcelotServiceApplication/OcelotApplicationService",
"UseServiceDiscovery" : true
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 19081,
"Type": "ServiceFabric"
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Fabric;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.ServiceFabric.Services.Communication.AspNetCore;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
using Microsoft.Extensions.Logging;
namespace OcelotApplicationService
{
/// <summary>
/// The FabricRuntime creates an instance of this class for each service type instance.
/// </summary>
internal sealed class ApiGateway : StatelessService
{
public ApiGateway(StatelessServiceContext context)
: base(context)
{ }
/// <summary>
/// Optional override to create listeners (like tcp, http) for this service instance.
/// </summary>
/// <returns>The collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext =>
new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
Console.WriteLine($"Starting Kestrel on {url}");
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");
return new WebHostBuilder()
.UseKestrel()
.ConfigureServices(
services => services
.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
.UseStartup<Startup>()
.UseUrls(url)
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Build();
}))
};
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace OcelotApplicationService.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Stateless Service Application</Description>
<Authors> </Authors>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>OcelotApplicationService</AssemblyName>
<PackageId>OcelotApplicationService</PackageId>
<PackageTargetFallback>$(PackageTargetFallback)</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>&#xD;
&#xD;
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ServiceFabric" Version="6.1.456"/>
<PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.0.456"/>
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.1"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.2"/>
<PackageReference Include="Microsoft.ServiceFabric.AspNetCore.Kestrel" Version="3.0.456"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,38 @@
using Microsoft.ServiceFabric.Services.Runtime;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace OcelotApplicationService
{
internal static class Program
{
/// <summary>
/// This is the entry point of the service host process.
/// </summary>
private static void Main()
{
try
{
// The ServiceManifest.XML file defines one or more service type names.
// Registering a service maps a service type name to a .NET type.
// When Service Fabric creates an instance of this service type,
// an instance of the class is created in this host process.
ServiceRuntime.RegisterServiceAsync("OcelotApplicationServiceType",
context => new ApiGateway(context)).GetAwaiter().GetResult();
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(ApiGateway).Name);
// Prevents this host process from terminating so services keeps running.
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}
}

View File

@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Fabric;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OcelotApplicationService
{
[EventSource(Name = "MyCompany-ServiceOcelotApplication-OcelotService")]
internal sealed class ServiceEventSource : EventSource
{
public static readonly ServiceEventSource Current = new ServiceEventSource();
static ServiceEventSource()
{
// A workaround for the problem where ETW activities do not get tracked until Tasks infrastructure is initialized.
// This problem will be fixed in .NET Framework 4.6.2.
Task.Run(() => { });
}
// Instance constructor is private to enforce singleton semantics
private ServiceEventSource() : base() { }
#region Keywords
// Event keywords can be used to categorize events.
// Each keyword is a bit flag. A single event can be associated with multiple keywords (via EventAttribute.Keywords property).
// Keywords must be defined as a public class named 'Keywords' inside EventSource that uses them.
public static class Keywords
{
public const EventKeywords Requests = (EventKeywords)0x1L;
public const EventKeywords ServiceInitialization = (EventKeywords)0x2L;
}
#endregion
#region Events
// Define an instance method for each event you want to record and apply an [Event] attribute to it.
// The method name is the name of the event.
// Pass any parameters you want to record with the event (only primitive integer types, DateTime, Guid & string are allowed).
// Each event method implementation should check whether the event source is enabled, and if it is, call WriteEvent() method to raise the event.
// The number and types of arguments passed to every event method must exactly match what is passed to WriteEvent().
// Put [NonEvent] attribute on all methods that do not define an event.
// For more information see https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource.aspx
[NonEvent]
public void Message(string message, params object[] args)
{
if (this.IsEnabled())
{
string finalMessage = string.Format(message, args);
Message(finalMessage);
}
}
private const int MessageEventId = 1;
[Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")]
public void Message(string message)
{
if (this.IsEnabled())
{
WriteEvent(MessageEventId, message);
}
}
[NonEvent]
public void ServiceMessage(ServiceContext serviceContext, string message, params object[] args)
{
if (this.IsEnabled())
{
string finalMessage = string.Format(message, args);
ServiceMessage(
serviceContext.ServiceName.ToString(),
serviceContext.ServiceTypeName,
GetReplicaOrInstanceId(serviceContext),
serviceContext.PartitionId,
serviceContext.CodePackageActivationContext.ApplicationName,
serviceContext.CodePackageActivationContext.ApplicationTypeName,
serviceContext.NodeContext.NodeName,
finalMessage);
}
}
// For very high-frequency events it might be advantageous to raise events using WriteEventCore API.
// This results in more efficient parameter handling, but requires explicit allocation of EventData structure and unsafe code.
// To enable this code path, define UNSAFE conditional compilation symbol and turn on unsafe code support in project properties.
private const int ServiceMessageEventId = 2;
[Event(ServiceMessageEventId, Level = EventLevel.Informational, Message = "{7}")]
private
#if UNSAFE
unsafe
#endif
void ServiceMessage(
string serviceName,
string serviceTypeName,
long replicaOrInstanceId,
Guid partitionId,
string applicationName,
string applicationTypeName,
string nodeName,
string message)
{
#if !UNSAFE
WriteEvent(ServiceMessageEventId, serviceName, serviceTypeName, replicaOrInstanceId, partitionId, applicationName, applicationTypeName, nodeName, message);
#else
const int numArgs = 8;
fixed (char* pServiceName = serviceName, pServiceTypeName = serviceTypeName, pApplicationName = applicationName, pApplicationTypeName = applicationTypeName, pNodeName = nodeName, pMessage = message)
{
EventData* eventData = stackalloc EventData[numArgs];
eventData[0] = new EventData { DataPointer = (IntPtr) pServiceName, Size = SizeInBytes(serviceName) };
eventData[1] = new EventData { DataPointer = (IntPtr) pServiceTypeName, Size = SizeInBytes(serviceTypeName) };
eventData[2] = new EventData { DataPointer = (IntPtr) (&replicaOrInstanceId), Size = sizeof(long) };
eventData[3] = new EventData { DataPointer = (IntPtr) (&partitionId), Size = sizeof(Guid) };
eventData[4] = new EventData { DataPointer = (IntPtr) pApplicationName, Size = SizeInBytes(applicationName) };
eventData[5] = new EventData { DataPointer = (IntPtr) pApplicationTypeName, Size = SizeInBytes(applicationTypeName) };
eventData[6] = new EventData { DataPointer = (IntPtr) pNodeName, Size = SizeInBytes(nodeName) };
eventData[7] = new EventData { DataPointer = (IntPtr) pMessage, Size = SizeInBytes(message) };
WriteEventCore(ServiceMessageEventId, numArgs, eventData);
}
#endif
}
private const int ServiceTypeRegisteredEventId = 3;
[Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)]
public void ServiceTypeRegistered(int hostProcessId, string serviceType)
{
WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
}
private const int ServiceHostInitializationFailedEventId = 4;
[Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)]
public void ServiceHostInitializationFailed(string exception)
{
WriteEvent(ServiceHostInitializationFailedEventId, exception);
}
// A pair of events sharing the same name prefix with a "Start"/"Stop" suffix implicitly marks boundaries of an event tracing activity.
// These activities can be automatically picked up by debugging and profiling tools, which can compute their execution time, child activities,
// and other statistics.
private const int ServiceRequestStartEventId = 5;
[Event(ServiceRequestStartEventId, Level = EventLevel.Informational, Message = "Service request '{0}' started", Keywords = Keywords.Requests)]
public void ServiceRequestStart(string requestTypeName)
{
WriteEvent(ServiceRequestStartEventId, requestTypeName);
}
private const int ServiceRequestStopEventId = 6;
[Event(ServiceRequestStopEventId, Level = EventLevel.Informational, Message = "Service request '{0}' finished", Keywords = Keywords.Requests)]
public void ServiceRequestStop(string requestTypeName, string exception = "")
{
WriteEvent(ServiceRequestStopEventId, requestTypeName, exception);
}
#endregion
#region Private methods
private static long GetReplicaOrInstanceId(ServiceContext context)
{
StatelessServiceContext stateless = context as StatelessServiceContext;
if (stateless != null)
{
return stateless.InstanceId;
}
StatefulServiceContext stateful = context as StatefulServiceContext;
if (stateful != null)
{
return stateful.ReplicaId;
}
throw new NotSupportedException("Context type not supported.");
}
#if UNSAFE
private int SizeInBytes(string s)
{
if (s == null)
{
return 0;
}
else
{
return (s.Length + 1) * sizeof(char);
}
}
#endif
#endregion
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace OcelotApplicationService
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
}

View File

@ -0,0 +1,6 @@
{
"projects": [ "../"],
"sdk": {
"version": "2.1.4"
}
}

View File

@ -0,0 +1,2 @@
Remove-ServiceFabricApplication fabric:/OcelotServiceApplication
Unregister-ServiceFabricApplicationType OcelotServiceApplicationType 1.0.0

View File

@ -0,0 +1,5 @@
#!/bin/bash -x
sfctl application delete --application-id OcelotServiceApplication
sfctl application unprovision --application-type-name OcelotServiceApplicationType --application-type-version 1.0.0
sfctl store delete --content-path OcelotServiceApplication

View File

@ -36,7 +36,6 @@ namespace Ocelot.Configuration.Builder
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
private string _upstreamHost;
private string _key;
public DownstreamReRouteBuilder()
{
_downstreamAddresses = new List<DownstreamHostAndPort>();

View File

@ -4,6 +4,7 @@ namespace Ocelot.Configuration.Builder
{
private string _serviceDiscoveryProviderHost;
private int _serviceDiscoveryProviderPort;
private string _type;
public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderHost(string serviceDiscoveryProviderHost)
{
@ -17,9 +18,15 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ServiceProviderConfigurationBuilder WithServiceDiscoveryProviderType(string type)
{
_type = type;
return this;
}
public ServiceProviderConfiguration Build()
{
return new ServiceProviderConfiguration(_serviceDiscoveryProviderHost,_serviceDiscoveryProviderPort);
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort);
}
}
}

View File

@ -7,11 +7,13 @@ namespace Ocelot.Configuration.Creator
{
public ServiceProviderConfiguration Create(FileGlobalConfiguration globalConfiguration)
{
//todo log or return error here dont just default to something that wont work..
var serviceProviderPort = globalConfiguration?.ServiceDiscoveryProvider?.Port ?? 0;
return new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderHost(globalConfiguration?.ServiceDiscoveryProvider?.Host)
.WithServiceDiscoveryProviderPort(serviceProviderPort)
.WithServiceDiscoveryProviderType(globalConfiguration?.ServiceDiscoveryProvider?.Type)
.Build();
}
}

View File

@ -4,5 +4,6 @@ namespace Ocelot.Configuration.File
{
public string Host {get;set;}
public int Port { get; set; }
public string Type { get; set; }
}
}

View File

@ -17,8 +17,8 @@ namespace Ocelot.Configuration.Repository
public ConsulFileConfigurationRepository(Cache.IOcelotCache<FileConfiguration> cache, ServiceProviderConfiguration serviceProviderConfig)
{
var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.ServiceProviderHost) ? "localhost" : serviceProviderConfig?.ServiceProviderHost;
var consulPort = serviceProviderConfig?.ServiceProviderPort ?? 8500;
var consulHost = string.IsNullOrEmpty(serviceProviderConfig?.Host) ? "localhost" : serviceProviderConfig?.Host;
var consulPort = serviceProviderConfig?.Port ?? 8500;
var configuration = new ConsulRegistryConfiguration(consulHost, consulPort, _ocelotConfiguration);
_cache = cache;
_consul = new ConsulClient(c =>

View File

@ -2,13 +2,15 @@
{
public class ServiceProviderConfiguration
{
public ServiceProviderConfiguration(string serviceProviderHost, int serviceProviderPort)
public ServiceProviderConfiguration(string type, string host, int port)
{
ServiceProviderHost = serviceProviderHost;
ServiceProviderPort = serviceProviderPort;
Host = host;
Port = port;
Type = type;
}
public string ServiceProviderHost { get; private set; }
public int ServiceProviderPort { get; private set; }
public string Host { get; private set; }
public int Port { get; private set; }
public string Type { get; private set; }
}
}

View File

@ -30,7 +30,7 @@ namespace Ocelot.Configuration.Validator
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes))
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct Key property");
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property");
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes))

View File

@ -99,7 +99,6 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
_services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
_services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
_services.TryAddSingleton<IUrlBuilder, UrlBuilder>();
_services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
_services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
_services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();

View File

@ -1,4 +1,5 @@
using Ocelot.Responses;
/*
using Ocelot.Responses;
using Ocelot.Values;
namespace Ocelot.DownstreamUrlCreator
@ -8,3 +9,4 @@ namespace Ocelot.DownstreamUrlCreator
Response<DownstreamUrl> Build(string downstreamPath, string downstreamScheme, ServiceHostAndPort downstreamHostAndPort);
}
}
*/

View File

@ -5,6 +5,7 @@ using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Middleware;
using System;
using System.Linq;
using Ocelot.DownstreamRouteFinder.Middleware;
namespace Ocelot.DownstreamUrlCreator.Middleware
@ -14,16 +15,13 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
private readonly OcelotRequestDelegate _next;
private readonly IDownstreamPathPlaceholderReplacer _replacer;
private readonly IOcelotLogger _logger;
private readonly IUrlBuilder _urlBuilder;
public DownstreamUrlCreatorMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IDownstreamPathPlaceholderReplacer replacer,
IUrlBuilder urlBuilder)
IDownstreamPathPlaceholderReplacer replacer)
{
_next = next;
_replacer = replacer;
_urlBuilder = urlBuilder;
_logger = loggerFactory.CreateLogger<DownstreamUrlCreatorMiddleware>();
}
@ -40,11 +38,32 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
return;
}
var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri)
UriBuilder uriBuilder;
//todo - feel this is a bit crap the way we build the url dont see why we need this builder thing..maybe i blew my own brains out
// when i originally wrote it..
if (context.ServiceProviderConfiguration.Type == "ServiceFabric" && context.DownstreamReRoute.UseServiceDiscovery)
{
_logger.LogInformation("DownstreamUrlCreatorMiddleware - going to try set service fabric path");
var scheme = context.DownstreamReRoute.DownstreamScheme;
var host = context.DownstreamRequest.RequestUri.Host;
var port = context.DownstreamRequest.RequestUri.Port;
var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
_logger.LogInformation("DownstreamUrlCreatorMiddleware - service fabric path is {proxyUrl}", serviceFabricPath);
var uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}?cmd=instance");
uriBuilder = new UriBuilder(uri);
}
else
{
uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri)
{
Path = dsPath.Data.Value,
Scheme = context.DownstreamReRoute.DownstreamScheme
};
}
context.DownstreamRequest.RequestUri = uriBuilder.Uri;

View File

@ -1,4 +1,5 @@
using System;
/*
using System;
using System.Collections.Generic;
using Ocelot.Errors;
using Ocelot.Responses;
@ -29,7 +30,7 @@ namespace Ocelot.DownstreamUrlCreator
{
Host = downstreamHostAndPort.DownstreamHost,
Path = downstreamPath,
Scheme = downstreamScheme
Scheme = downstreamScheme,
};
if (downstreamHostAndPort.DownstreamPort > 0)
@ -43,3 +44,4 @@ namespace Ocelot.DownstreamUrlCreator
}
}
}
*/

View File

@ -17,6 +17,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public async Task<Response<ServiceHostAndPort>> Lease()
{
//todo no point spinning a task up here, also first or default could be null..
var service = await Task.FromResult(_services.FirstOrDefault());
return new OkResponse<ServiceHostAndPort>(service.HostAndPort);
}

View File

@ -18,7 +18,7 @@ namespace Ocelot.ServiceDiscovery
{
if (reRoute.UseServiceDiscovery)
{
return GetServiceDiscoveryProvider(reRoute.ServiceName, serviceConfig.ServiceProviderHost, serviceConfig.ServiceProviderPort);
return GetServiceDiscoveryProvider(serviceConfig, reRoute.ServiceName);
}
var services = new List<Service>();
@ -33,9 +33,15 @@ namespace Ocelot.ServiceDiscovery
return new ConfigurationServiceProvider(services);
}
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string keyOfServiceInConsul, string providerHostName, int providerPort)
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(ServiceProviderConfiguration serviceConfig, string serviceName)
{
var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, keyOfServiceInConsul);
if (serviceConfig.Type == "ServiceFabric")
{
var config = new ServiceFabricConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName);
return new ServiceFabricServiceDiscoveryProvider(config);
}
var consulRegistryConfiguration = new ConsulRegistryConfiguration(serviceConfig.Host, serviceConfig.Port, serviceName);
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory);
}
}

View File

@ -0,0 +1,16 @@
namespace Ocelot.ServiceDiscovery
{
public class ServiceFabricConfiguration
{
public ServiceFabricConfiguration(string hostName, int port, string serviceName)
{
HostName = hostName;
Port = port;
ServiceName = serviceName;
}
public string ServiceName { get; private set; }
public string HostName { get; private set; }
public int Port { get; private set; }
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ocelot.Values;
namespace Ocelot.ServiceDiscovery
{
public class ServiceFabricServiceDiscoveryProvider : IServiceDiscoveryProvider
{
private readonly ServiceFabricConfiguration _configuration;
public ServiceFabricServiceDiscoveryProvider(ServiceFabricConfiguration configuration)
{
_configuration = configuration;
}
public async Task<List<Service>> Get()
{
return new List<Service>
{
new Service(_configuration.ServiceName,
new ServiceHostAndPort(_configuration.HostName, _configuration.Port),
"doesnt matter with service fabric",
"doesnt matter with service fabric",
new List<string>())
};
}
}
}

View File

@ -0,0 +1,112 @@
using System.Linq;
using Microsoft.Extensions.Primitives;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class ServiceFabricTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
public ServiceFabricTests()
{
_steps = new Steps();
}
[Fact]
public void should_support_service_fabric_naming_and_dns_service()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
UpstreamPathTemplate = "/EquipmentInterfaces",
UpstreamHttpMethod = new List<string> { "Get" },
UseServiceDiscovery = true,
ServiceName = "OcelotServiceApplication/OcelotApplicationService"
}
},
GlobalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = 19081,
Type = "ServiceFabric"
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:19081", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
if (context.Request.Query.TryGetValue("cmd", out var values))
{
values.First().ShouldBe("instance");
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
}
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -328,7 +328,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenAConfiguration(configuration))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "ReRoutes for aggregateReRoute / either do not exist or do not have correct Key property"))
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "ReRoutes for aggregateReRoute / either do not exist or do not have correct ServiceName property"))
.BDDfy();
}

View File

@ -30,13 +30,15 @@ namespace Ocelot.UnitTests.Configuration
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "127.0.0.1",
Port = 1234
Port = 1234,
Type = "ServiceFabric"
}
};
var expected = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderHost("127.0.0.1")
.WithServiceDiscoveryProviderPort(1234)
.WithServiceDiscoveryProviderType("ServiceFabric")
.Build();
this.Given(x => x.GivenTheFollowingReRoute(reRoute))
@ -63,8 +65,8 @@ namespace Ocelot.UnitTests.Configuration
private void ThenTheConfigIs(ServiceProviderConfiguration expected)
{
_result.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost);
_result.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort);
_result.Host.ShouldBe(expected.Host);
_result.Port.ShouldBe(expected.Port);
}
}
}

View File

@ -1,4 +1,5 @@
using Ocelot.Middleware;
using Ocelot.Configuration;
using Ocelot.Middleware;
namespace Ocelot.UnitTests.DownstreamUrlCreator
{
@ -27,7 +28,6 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
public class DownstreamUrlCreatorMiddlewareTests
{
private readonly Mock<IDownstreamPathPlaceholderReplacer> _downstreamUrlTemplateVariableReplacer;
private readonly Mock<IUrlBuilder> _urlBuilder;
private OkResponse<DownstreamPath> _downstreamPath;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
@ -42,7 +42,6 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
_urlBuilder = new Mock<IUrlBuilder>();
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
_next = async context => {
//do nothing
@ -58,6 +57,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.WithDownstreamScheme("https")
.Build();
var config = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(
new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
@ -66,15 +68,81 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
.BDDfy();
}
[Fact]
public void should_not_create_service_fabric_url()
{
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamPathTemplate("any old string")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithDownstreamScheme("https")
.Build();
var config = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderType("ServiceFabric")
.WithServiceDiscoveryProviderHost("localhost")
.WithServiceDiscoveryProviderPort(19081)
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(
new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123"))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123"))
.BDDfy();
}
[Fact]
public void should_create_service_fabric_url()
{
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamScheme("http")
.WithServiceName("Ocelot/OcelotApp")
.WithUseServiceDiscovery(true)
.Build();
var downstreamRoute = new DownstreamRoute(
new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.Build());
var config = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderType("ServiceFabric")
.WithServiceDiscoveryProviderHost("localhost")
.WithServiceDiscoveryProviderPort(19081)
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081"))
.And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?cmd=instance"))
.BDDfy();
}
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
{
_downstreamContext.ServiceProviderConfiguration = config;
}
private void WhenICallTheMiddleware()
{
_middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object, _urlBuilder.Object);
_middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}

View File

@ -1,122 +0,0 @@
using System;
using Ocelot.DownstreamUrlCreator;
using Ocelot.Responses;
using Ocelot.Values;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.DownstreamUrlCreator
{
public class UrlBuilderTests
{
private readonly IUrlBuilder _urlBuilder;
private string _dsPath;
private string _dsScheme;
private string _dsHost;
private int _dsPort;
private Response<DownstreamUrl> _result;
public UrlBuilderTests()
{
_urlBuilder = new UrlBuilder();
}
[Fact]
public void should_return_error_when_downstream_path_is_null()
{
this.Given(x => x.GivenADownstreamPath(null))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenThereIsAnErrorOfType<DownstreamPathNullOrEmptyError>())
.BDDfy();
}
[Fact]
public void should_return_error_when_downstream_scheme_is_null()
{
this.Given(x => x.GivenADownstreamScheme(null))
.And(x => x.GivenADownstreamPath("test"))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenThereIsAnErrorOfType<DownstreamSchemeNullOrEmptyError>())
.BDDfy();
}
[Fact]
public void should_return_error_when_downstream_host_is_null()
{
this.Given(x => x.GivenADownstreamScheme(null))
.And(x => x.GivenADownstreamPath("test"))
.And(x => x.GivenADownstreamScheme("test"))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenThereIsAnErrorOfType<DownstreamHostNullOrEmptyError>())
.BDDfy();
}
[Fact]
public void should_not_use_port_if_zero()
{
this.Given(x => x.GivenADownstreamPath("/api/products/1"))
.And(x => x.GivenADownstreamScheme("http"))
.And(x => x.GivenADownstreamHost("127.0.0.1"))
.And(x => x.GivenADownstreamPort(0))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenTheUrlIsReturned("http://127.0.0.1/api/products/1"))
.And(x => x.ThenTheUrlIsWellFormed())
.BDDfy();
}
[Fact]
public void should_build_well_formed_uri()
{
this.Given(x => x.GivenADownstreamPath("/api/products/1"))
.And(x => x.GivenADownstreamScheme("http"))
.And(x => x.GivenADownstreamHost("127.0.0.1"))
.And(x => x.GivenADownstreamPort(5000))
.When(x => x.WhenIBuildTheUrl())
.Then(x => x.ThenTheUrlIsReturned("http://127.0.0.1:5000/api/products/1"))
.And(x => x.ThenTheUrlIsWellFormed())
.BDDfy();
}
private void ThenThereIsAnErrorOfType<T>()
{
_result.Errors[0].ShouldBeOfType<T>();
}
private void GivenADownstreamPath(string dsPath)
{
_dsPath = dsPath;
}
private void GivenADownstreamScheme(string dsScheme)
{
_dsScheme = dsScheme;
}
private void GivenADownstreamHost(string dsHost)
{
_dsHost = dsHost;
}
private void GivenADownstreamPort(int dsPort)
{
_dsPort = dsPort;
}
private void WhenIBuildTheUrl()
{
_result = _urlBuilder.Build(_dsPath, _dsScheme, new ServiceHostAndPort(_dsHost, _dsPort));
}
private void ThenTheUrlIsReturned(string expected)
{
_result.Data.Value.ShouldBe(expected);
}
private void ThenTheUrlIsWellFormed()
{
Uri.IsWellFormedUriString(_result.Data.Value, UriKind.Absolute).ShouldBeTrue();
}
}
}

View File

@ -0,0 +1,58 @@
namespace Ocelot.UnitTests.ServiceDiscovery
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.Values;
using Xunit;
using TestStack.BDDfy;
using Shouldly;
public class ServiceFabricServiceDiscoveryProviderTests
{
private ServiceFabricServiceDiscoveryProvider _provider;
private ServiceFabricConfiguration _config;
private string _host;
private string _serviceName;
private int _port;
private List<Service> _services;
[Fact]
public void should_return_service_fabric_naming_service()
{
this.Given(x => GivenTheFollowing())
.When(x => WhenIGet())
.Then(x => ThenTheServiceFabricNamingServiceIsRetured())
.BDDfy();
}
private void GivenTheFollowing()
{
_host = "localhost";
_serviceName = "OcelotServiceApplication/OcelotApplicationService";
_port = 19081;
}
private void WhenIGet()
{
_config = new ServiceFabricConfiguration(_host, _port, _serviceName);
_provider = new ServiceFabricServiceDiscoveryProvider(_config);
_services = _provider.Get().GetAwaiter().GetResult();
}
private void ThenTheServiceFabricNamingServiceIsRetured()
{
_services.Count.ShouldBe(1);
_services[0].HostAndPort.DownstreamHost.ShouldBe(_host);
_services[0].HostAndPort.DownstreamPort.ShouldBe(_port);
}
}
}

View File

@ -77,6 +77,24 @@ namespace Ocelot.UnitTests.ServiceDiscovery
.BDDfy();
}
[Fact]
public void should_return_service_fabric_provider()
{
var reRoute = new DownstreamReRouteBuilder()
.WithServiceName("product")
.WithUseServiceDiscovery(true)
.Build();
var serviceConfig = new ServiceProviderConfigurationBuilder()
.WithServiceDiscoveryProviderType("ServiceFabric")
.Build();
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<ServiceFabricServiceDiscoveryProvider>())
.BDDfy();
}
private void ThenTheFollowingServicesAreReturned(List<DownstreamHostAndPort> downstreamAddresses)
{
var result = (ConfigurationServiceProvider)_result;