From 0f6f4e6d14c2e5a8aef3f5a818fbcc00df0ac29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Fri, 30 Nov 2018 17:43:22 +0100 Subject: [PATCH] Integrate HealthCheck to all eShop services and apps Integrate WebStatus with Healthcheck --- NuGet.config | 3 + docker-compose.override.yml | 89 +++++++- eShopOnContainers-ServicesAndWebApps.sln | 207 ------------------ src/ApiGateways/ApiGw-Base/Dockerfile | 3 +- src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj | 5 +- src/ApiGateways/ApiGw-Base/Startup.cs | 18 ++ .../Mobile.Shopping.HttpAggregator.csproj | 3 + .../Mobile.Bff.Shopping/aggregator/Startup.cs | 17 ++ .../Web.Bff.Shopping/aggregator/Startup.cs | 17 ++ .../Web.Shopping.HttpAggregator.csproj | 2 + .../HealthCheckMiddleware.cs | 76 ------- .../HealthCheckStartupFilter.cs | 45 ---- .../HealthCheckWebHostBuilderExtension.cs | 47 ---- .../HealthCheckWebHostExtensions.cs | 38 ---- .../Microsoft.AspNetCore.HealthChecks.csproj | 19 -- .../AzureHealthCheckBuilderExtensions.cs | 175 --------------- ...xtensions.HealthChecks.AzureStorage.csproj | 26 --- .../Properties/AssemblyInfo.cs | 22 -- .../HealthCheckBuilderSqlServerExtensions.cs | 54 ----- ...t.Extensions.HealthChecks.SqlServer.csproj | 19 -- .../CachedHealthCheck.cs | 109 --------- .../CachedHealthCheckExtensions.cs | 19 -- .../CheckStatus.cs | 13 -- .../Checks/AddCheck.cs | 116 ---------- .../Checks/NumericChecks.cs | 69 ------ .../Checks/SystemChecks.cs | 31 --- .../Checks/UrlChecks.cs | 83 ------- .../CompositeHealthCheckResult.cs | 86 -------- .../HealthCheck.cs | 42 ---- .../HealthCheckBuilder.cs | 135 ------------ .../HealthCheckGroup.cs | 37 ---- .../HealthCheckResult.cs | 53 ----- .../HealthCheckResults.cs | 12 - .../HealthCheckService.cs | 119 ---------- .../HealthCheckServiceCollectionExtensions.cs | 30 --- .../IHealthCheck.cs | 13 -- .../IHealthCheckResult.cs | 14 -- .../IHealthCheckService.cs | 58 ----- .../Internal/UrlChecker.cs | 68 ------ .../Microsoft.Extensions.HealthChecks.csproj | 20 -- .../HealthChecks/src/common/Guard.cs | 57 ----- .../Basket/Basket.API/Basket.API.csproj | 9 +- src/Services/Basket/Basket.API/Program.cs | 1 - src/Services/Basket/Basket.API/Startup.cs | 51 ++++- .../Catalog/Catalog.API/Catalog.API.csproj | 10 +- src/Services/Catalog/Catalog.API/Program.cs | 1 - src/Services/Catalog/Catalog.API/Startup.cs | 74 +++++-- .../Identity/Identity.API/Identity.API.csproj | 6 +- src/Services/Identity/Identity.API/Program.cs | 1 - src/Services/Identity/Identity.API/Startup.cs | 24 +- .../Locations.API/Locations.API.csproj | 7 +- .../Location/Locations.API/Program.cs | 1 - .../Location/Locations.API/Startup.cs | 55 ++++- .../Marketing.API/Marketing.API.csproj | 10 +- .../Marketing/Marketing.API/Program.cs | 1 - .../Marketing/Marketing.API/Startup.cs | 85 +++++-- .../Ordering/Ordering.API/Ordering.API.csproj | 10 +- src/Services/Ordering/Ordering.API/Program.cs | 1 - src/Services/Ordering/Ordering.API/Startup.cs | 42 +++- .../Ordering.BackgroundTasks.csproj | 8 +- .../Ordering.BackgroundTasks/Program.cs | 3 +- .../Ordering.BackgroundTasks/Startup.cs | 53 ++++- .../Ordering.SignalrHub.csproj | 4 + .../Ordering/Ordering.SignalrHub/Startup.cs | 55 ++++- .../Payment/Payment.API/Payment.API.csproj | 6 +- src/Services/Payment/Payment.API/Program.cs | 1 - src/Services/Payment/Payment.API/Startup.cs | 53 ++++- src/Web/WebMVC/Program.cs | 1 - src/Web/WebMVC/Startup.cs | 30 ++- src/Web/WebMVC/WebMVC.csproj | 10 +- src/Web/WebSPA/Program.cs | 1 - src/Web/WebSPA/Startup.cs | 28 +-- src/Web/WebSPA/WebSPA.csproj | 7 +- .../WebStatus/Controllers/HomeController.cs | 21 +- .../HealthCheckBuilderExtensions.cs | 18 -- src/Web/WebStatus/Program.cs | 6 +- src/Web/WebStatus/Startup.cs | 40 +--- .../Viewmodels/HealthStatusViewModel.cs | 23 -- .../WebStatus/Viewmodels/NamedCheckResult.cs | 17 -- src/Web/WebStatus/Views/Home/Index.cshtml | 51 ----- src/Web/WebStatus/Views/Shared/Error.cshtml | 14 -- src/Web/WebStatus/Views/Shared/_Layout.cshtml | 60 ----- src/Web/WebStatus/Views/_ViewImports.cshtml | 2 - src/Web/WebStatus/Views/_ViewStart.cshtml | 3 - src/Web/WebStatus/WebStatus.csproj | 8 +- src/Web/WebStatus/appsettings.json | 99 +++++++-- 86 files changed, 706 insertions(+), 2374 deletions(-) delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostExtensions.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Microsoft.Extensions.HealthChecks.AzureStorage.csproj delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Properties/AssemblyInfo.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheck.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheckExtensions.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CheckStatus.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckGroup.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResults.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckResult.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs delete mode 100644 src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj delete mode 100644 src/BuildingBlocks/HealthChecks/src/common/Guard.cs delete mode 100644 src/Web/WebStatus/Extensions/HealthCheckBuilderExtensions.cs delete mode 100644 src/Web/WebStatus/Viewmodels/HealthStatusViewModel.cs delete mode 100644 src/Web/WebStatus/Viewmodels/NamedCheckResult.cs delete mode 100644 src/Web/WebStatus/Views/Home/Index.cshtml delete mode 100644 src/Web/WebStatus/Views/Shared/Error.cshtml delete mode 100644 src/Web/WebStatus/Views/Shared/_Layout.cshtml delete mode 100644 src/Web/WebStatus/Views/_ViewImports.cshtml delete mode 100644 src/Web/WebStatus/Views/_ViewStart.cshtml diff --git a/NuGet.config b/NuGet.config index da29646ef..0a3819dd2 100644 --- a/NuGet.config +++ b/NuGet.config @@ -4,6 +4,9 @@ + + + diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 969cfb922..c9313d80c 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -187,6 +187,13 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - CatalogUrlHC=http://catalog.api/hc + - OrderingUrlHC=http://ordering.api/hc + - IdentityUrlHC=http://identity.api/hc + - BasketUrlHC=http://basket.api/hc + - MarketingUrlHC=http://marketing.api/hc + - PaymentUrlHC=http://payment.api/hc + - LocationUrlHC=http://locations.api/hc ports: - "5200:80" volumes: @@ -196,6 +203,13 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - CatalogUrlHC=http://catalog.api/hc + - OrderingUrlHC=http://ordering.api/hc + - IdentityUrlHC=http://identity.api/hc + - BasketUrlHC=http://basket.api/hc + - MarketingUrlHC=http://marketing.api/hc + - PaymentUrlHC=http://payment.api/hc + - LocationUrlHC=http://locations.api/hc ports: - "5201:80" volumes: @@ -205,6 +219,13 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - CatalogUrlHC=http://catalog.api/hc + - OrderingUrlHC=http://ordering.api/hc + - IdentityUrlHC=http://identity.api/hc + - BasketUrlHC=http://basket.api/hc + - MarketingUrlHC=http://marketing.api/hc + - PaymentUrlHC=http://payment.api/hc + - LocationUrlHC=http://locations.api/hc ports: - "5202:80" volumes: @@ -214,6 +235,13 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - CatalogUrlHC=http://catalog.api/hc + - OrderingUrlHC=http://ordering.api/hc + - IdentityUrlHC=http://identity.api/hc + - BasketUrlHC=http://basket.api/hc + - MarketingUrlHC=http://marketing.api/hc + - PaymentUrlHC=http://payment.api/hc + - LocationUrlHC=http://locations.api/hc ports: - "5203:80" volumes: @@ -226,6 +254,13 @@ services: - urls__catalog=http://catalog.api - urls__orders=http://ordering.api - urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - CatalogUrlHC=http://catalog.api/hc + - OrderingUrlHC=http://ordering.api/hc + - IdentityUrlHC=http://identity.api/hc + - BasketUrlHC=http://basket.api/hc + - MarketingUrlHC=http://marketing.api/hc + - PaymentUrlHC=http://payment.api/hc + - LocationUrlHC=http://locations.api/hc ports: - "5120:80" # Important: In a production environment your should remove the external port (5120) kept here for microservice debugging purposes. # The API Gateway redirects and access through the internal port (80). @@ -237,6 +272,13 @@ services: - urls__catalog=http://catalog.api - urls__orders=http://ordering.api - urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + - CatalogUrlHC=http://catalog.api/hc + - OrderingUrlHC=http://ordering.api/hc + - IdentityUrlHC=http://identity.api/hc + - BasketUrlHC=http://basket.api/hc + - MarketingUrlHC=http://marketing.api/hc + - PaymentUrlHC=http://payment.api/hc + - LocationUrlHC=http://locations.api/hc ports: - "5121:80" # Important: In a production environment your should remove the external port (5121) kept here for microservice debugging purposes. # The API Gateway redirects and access through the internal port (80). @@ -260,7 +302,41 @@ services: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - CatalogUrl=http://catalog.api/hc - - OrderingUrl=http://ordering.api/hc + - HealthChecks-UI__HealthChecks__0__Name=Ordering HTTP Check + - HealthChecks-UI__HealthChecks__0__Uri=http://ordering.api/hc + - HealthChecks-UI__HealthChecks__1__Name=Ordering HTTP Background Check + - HealthChecks-UI__HealthChecks__1__Uri=http://ordering.backgroundtasks/hc + - HealthChecks-UI__HealthChecks__2__Name=Basket HTTP Background Check + - HealthChecks-UI__HealthChecks__2__Uri=http://basket.api/hc + - HealthChecks-UI__HealthChecks__3__Name=Catalog HTTP Check + - HealthChecks-UI__HealthChecks__3__Uri=http://catalog.api/hc + - HealthChecks-UI__HealthChecks__4__Name=Identity HTTP Check + - HealthChecks-UI__HealthChecks__4__Uri=http://identity.api/hc + - HealthChecks-UI__HealthChecks__5__Name=Marketing HTTP Check + - HealthChecks-UI__HealthChecks__5__Uri=http://marketing.api/hc + - HealthChecks-UI__HealthChecks__6__Name=Locations HTTP Check + - HealthChecks-UI__HealthChecks__6__Uri=http://locations.api/hc + - HealthChecks-UI__HealthChecks__7__Name=Payments HTTP Check + - HealthChecks-UI__HealthChecks__7__Uri=http://payment.api/hc + - HealthChecks-UI__HealthChecks__8__Name=Ordering SignalRHub HTTP Check + - HealthChecks-UI__HealthChecks__8__Uri=http://ordering.signalrhub/hc + - HealthChecks-UI__HealthChecks__9__Name=WebMVC HTTP Check + - HealthChecks-UI__HealthChecks__9__Uri=http://webmvc/hc + - HealthChecks-UI__HealthChecks__10__Name=WebSPA HTTP Check + - HealthChecks-UI__HealthChecks__10__Uri=http://webspa/hc + - HealthChecks-UI__HealthChecks__11__Name=Mobile Shopping API GW HTTP Check + - HealthChecks-UI__HealthChecks__11__Uri=http://mobileshoppingapigw/hc + - HealthChecks-UI__HealthChecks__12__Name=Mobile Marketing API GW HTTP Check + - HealthChecks-UI__HealthChecks__12__Uri=http://mobilemarketingapigw/hc + - HealthChecks-UI__HealthChecks__13__Name=Web Shopping API GW HTTP Check + - HealthChecks-UI__HealthChecks__13__Uri=http://webshoppingapigw/hc + - HealthChecks-UI__HealthChecks__14__Name=Web Marketing API GW HTTP Check + - HealthChecks-UI__HealthChecks__14__Uri=http://webmarketingapigw/hc + - HealthChecks-UI__HealthChecks__15__Name=Mobile Shopping Aggregator HTTP Check + - HealthChecks-UI__HealthChecks__15__Uri=http://mobileshoppingagg/hc + - HealthChecks-UI__HealthChecks__16__Name=Web Shopping API GW HTTP Check + - HealthChecks-UI__HealthChecks__16__Uri=http://webshoppingagg/hc + - OrderingBackgroundTasksUrl=http://ordering.backgroundtasks/hc - BasketUrl=http://basket.api/hc - IdentityUrl=http://identity.api/hc @@ -300,14 +376,11 @@ services: - ASPNETCORE_URLS=http://0.0.0.0:80 - PurchaseUrl=http://webshoppingapigw - IdentityUrl=http://10.0.75.1:5105 # Local Mac: Use http://docker.for.mac.localhost:5105 || Local Windows: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. || #Remote access: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser. - - MarketingUrl=http://webmarketingapigw - - CatalogUrlHC=http://catalog.api/hc - - OrderingUrlHC=http://ordering.api/hc - - IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser. - - BasketUrlHC=http://basket.api/hc - - MarketingUrlHC=http://marketing.api/hc - - PaymentUrlHC=http://payment.api/hc + - MarketingUrl=http://webmarketingapigw - SignalrHubUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5202 + - IdentityUrlHC=http://identity.api/hc + - PurchaseUrlHC=http://webshoppingapigw/hc + - MarketingUrlHC=http://webmarketingapigw/hc - UseCustomizationData=True - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} - OrchestratorType=${ORCHESTRATOR_TYPE} diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 583b4499c..0a152c73b 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -56,16 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBusRabbitMQ", "src\Bui EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationEventLogEF", "src\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj", "{9EE28E45-1533-472B-8267-56C48855BA0E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{A81ECBC2-6B00-4DCD-8388-469174033379}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj", "{942ED6E8-0050-495F-A0EA-01E97F63760C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HealthChecks", "src\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj", "{22A0F9C1-2D4A-4107-95B7-8459E6688BC5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.SqlServer", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj", "{4BD76717-3102-4969-8C2C-BAAA3F0263B6}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Location", "Location", "{41139F64-4046-4F16-96B7-D941D96FA9C6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Locations.API", "src\Services\Location\Locations.API\Locations.API.csproj", "{E7581357-FC34-474C-B8F5-307EE3CE05EF}" @@ -76,8 +68,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.API", "src\Servic EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBusServiceBus", "src\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj", "{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HealthChecks.AzureStorage", "src\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.AzureStorage\Microsoft.Extensions.HealthChecks.AzureStorage.csproj", "{768C887F-C229-4B94-ACD8-0C7F65686524}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebHost", "WebHost", "{1815B651-941C-466B-AE33-D1D7EEB8F77F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebHost.Customization", "src\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj", "{15F4B3AA-89B6-4A0D-9051-414305974781}" @@ -644,54 +634,6 @@ Global {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.Build.0 = Release|Any CPU {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.ActiveCfg = Release|Any CPU {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.Build.0 = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|ARM.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhone.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x64.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x64.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x86.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.AppStore|x86.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|ARM.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|ARM.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhone.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x64.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x64.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x86.ActiveCfg = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Debug|x86.Build.0 = Debug|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|Any CPU.Build.0 = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|ARM.ActiveCfg = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|ARM.Build.0 = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhone.ActiveCfg = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhone.Build.0 = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.ActiveCfg = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x64.Build.0 = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.ActiveCfg = Release|Any CPU - {942ED6E8-0050-495F-A0EA-01E97F63760C}.Release|x86.Build.0 = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -740,102 +682,6 @@ Global {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|ARM.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhone.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x64.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x64.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x86.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.AppStore|x86.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|ARM.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|ARM.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhone.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x64.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x64.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x86.ActiveCfg = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Debug|x86.Build.0 = Debug|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|Any CPU.Build.0 = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|ARM.ActiveCfg = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|ARM.Build.0 = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhone.ActiveCfg = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhone.Build.0 = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x64.ActiveCfg = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x64.Build.0 = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x86.ActiveCfg = Release|Any CPU - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5}.Release|x86.Build.0 = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|ARM.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhone.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x64.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x64.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x86.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.AppStore|x86.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|ARM.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|ARM.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhone.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x64.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x64.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x86.ActiveCfg = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Debug|x86.Build.0 = Debug|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|Any CPU.Build.0 = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|ARM.ActiveCfg = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|ARM.Build.0 = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhone.ActiveCfg = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhone.Build.0 = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.ActiveCfg = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x64.Build.0 = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.ActiveCfg = Release|Any CPU - {4BD76717-3102-4969-8C2C-BAAA3F0263B6}.Release|x86.Build.0 = Release|Any CPU {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -980,54 +826,6 @@ Global {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x64.Build.0 = Release|Any CPU {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x86.ActiveCfg = Release|Any CPU {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x86.Build.0 = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|ARM.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhone.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x64.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x64.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x86.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.AppStore|x86.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|Any CPU.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|ARM.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|ARM.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhone.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x64.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x64.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x86.ActiveCfg = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Debug|x86.Build.0 = Debug|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|Any CPU.ActiveCfg = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|Any CPU.Build.0 = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|ARM.ActiveCfg = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|ARM.Build.0 = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhone.ActiveCfg = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhone.Build.0 = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x64.ActiveCfg = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x64.Build.0 = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x86.ActiveCfg = Release|Any CPU - {768C887F-C229-4B94-ACD8-0C7F65686524}.Release|x86.Build.0 = Release|Any CPU {15F4B3AA-89B6-4A0D-9051-414305974781}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {15F4B3AA-89B6-4A0D-9051-414305974781}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {15F4B3AA-89B6-4A0D-9051-414305974781}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU @@ -1869,17 +1667,12 @@ Global {0044B293-1DCC-4224-B948-00CF6DC7F510} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} {8088F3FC-6787-45FA-A924-816EC81CBFAC} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} {9EE28E45-1533-472B-8267-56C48855BA0E} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} - {A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} - {942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379} {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} - {22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379} - {4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379} {41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} {E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6} {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} {DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} - {768C887F-C229-4B94-ACD8-0C7F65686524} = {A81ECBC2-6B00-4DCD-8388-469174033379} {1815B651-941C-466B-AE33-D1D7EEB8F77F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {15F4B3AA-89B6-4A0D-9051-414305974781} = {1815B651-941C-466B-AE33-D1D7EEB8F77F} {77849D35-37D4-4802-81DC-9477B2775A40} = {932D8224-11F6-4D07-B109-DA28AD288A63} diff --git a/src/ApiGateways/ApiGw-Base/Dockerfile b/src/ApiGateways/ApiGw-Base/Dockerfile index f3e57b18d..f9d2e3842 100644 --- a/src/ApiGateways/ApiGw-Base/Dockerfile +++ b/src/ApiGateways/ApiGw-Base/Dockerfile @@ -4,10 +4,9 @@ EXPOSE 80 FROM microsoft/dotnet:2.2.100-preview3-sdk AS build WORKDIR /src -COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ -RUN dotnet restore src/ApiGateways/ApiGw-Base/ COPY . . WORKDIR /src/src/ApiGateways/ApiGw-Base/ +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build -c Release -o /app FROM build AS publish diff --git a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj index ec9f36ef1..835639ef2 100644 --- a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj +++ b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj @@ -9,7 +9,10 @@ - + + + + diff --git a/src/ApiGateways/ApiGw-Base/Startup.cs b/src/ApiGateways/ApiGw-Base/Startup.cs index a574999c1..38dcf1d52 100644 --- a/src/ApiGateways/ApiGw-Base/Startup.cs +++ b/src/ApiGateways/ApiGw-Base/Startup.cs @@ -1,10 +1,13 @@ using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Ocelot.DependencyInjection; using Ocelot.Middleware; +using System; +using HealthChecks.UI.Client; namespace OcelotApiGw { @@ -22,6 +25,15 @@ namespace OcelotApiGw var identityUrl = _cfg.GetValue("IdentityUrl"); var authenticationProviderKey = "IdentityApiKey"; + services.AddHealthChecks() + .AddUrlGroup(new Uri(_cfg["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) + .AddUrlGroup(new Uri(_cfg["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) + .AddUrlGroup(new Uri(_cfg["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) + .AddUrlGroup(new Uri(_cfg["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) + .AddUrlGroup(new Uri(_cfg["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" }) + .AddUrlGroup(new Uri(_cfg["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }) + .AddUrlGroup(new Uri(_cfg["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" }); + services.AddCors(options => { options.AddPolicy("CorsPolicy", @@ -75,6 +87,12 @@ namespace OcelotApiGw app.UseDeveloperExceptionPage(); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + loggerFactory.AddConsole(_cfg.GetSection("Logging")); app.UseCors("CorsPolicy"); diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj index c393cef3e..8adc50166 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj @@ -12,7 +12,10 @@ + + + diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs index 4fe3b4e3b..2a19dde84 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs @@ -17,6 +17,8 @@ using Microsoft.Extensions.Logging; using Polly; using Polly.Extensions.Http; using Swashbuckle.AspNetCore.Swagger; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator { @@ -32,6 +34,15 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddHealthChecks() + .AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) + .AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) + .AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) + .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) + .AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" }) + .AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }) + .AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" }); + services.AddCustomMvc(Configuration) .AddCustomAuthentication(Configuration) .AddHttpServices(); @@ -48,6 +59,12 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator app.UsePathBase(pathBase); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + app.UseCors("CorsPolicy"); if (env.IsDevelopment()) diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs index 23db1f912..a4777774e 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs @@ -18,6 +18,8 @@ using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Net.Http; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator { @@ -33,6 +35,15 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddHealthChecks() + .AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) + .AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) + .AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) + .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) + .AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" }) + .AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }) + .AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" }); + services.AddCustomMvc(Configuration) .AddCustomAuthentication(Configuration) .AddApplicationServices(); @@ -48,6 +59,12 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator app.UsePathBase(pathBase); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + app.UseCors("CorsPolicy"); if (env.IsDevelopment()) diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj index 55e5e5286..0d549d33f 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj @@ -12,6 +12,8 @@ + + diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs deleted file mode 100644 index f8e68c957..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckMiddleware.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.HealthChecks; -using Newtonsoft.Json; - -namespace Microsoft.AspNetCore.HealthChecks -{ - public class HealthCheckMiddleware - { - private readonly RequestDelegate _next; - private readonly string _path; - private readonly int? _port; - private readonly IHealthCheckService _service; - private readonly TimeSpan _timeout; - - public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port, TimeSpan timeout) - { - _port = port; - _service = service; - _next = next; - _timeout = timeout; - } - - public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path, TimeSpan timeout) - { - _path = path; - _service = service; - _next = next; - _timeout = timeout; - } - - public async Task Invoke(HttpContext context) - { - if (IsHealthCheckRequest(context)) - { - var timeoutTokenSource = new CancellationTokenSource(_timeout); - var result = await _service.CheckHealthAsync(timeoutTokenSource.Token); - var status = result.CheckStatus; - - if (status != CheckStatus.Healthy) - context.Response.StatusCode = 503; - - context.Response.Headers.Add("content-type", "application/json"); - await context.Response.WriteAsync(JsonConvert.SerializeObject(new { status = status.ToString() })); - return; - } - else - { - await _next.Invoke(context); - } - } - - private bool IsHealthCheckRequest(HttpContext context) - { - if (_port.HasValue) - { - var connInfo = context.Features.Get(); - if (connInfo.LocalPort == _port) - return true; - } - - if (context.Request.Path == _path) - { - return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs deleted file mode 100644 index cac4b1188..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckStartupFilter.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; - -namespace Microsoft.AspNetCore.HealthChecks -{ - public class HealthCheckStartupFilter : IStartupFilter - { - private string _path; - private int? _port; - private TimeSpan _timeout; - - public HealthCheckStartupFilter(int port, TimeSpan timeout) - { - _port = port; - _timeout = timeout; - } - - public HealthCheckStartupFilter(string path, TimeSpan timeout) - { - _path = path; - _timeout = timeout; - } - - public Action Configure(Action next) - { - return app => - { - if (_port.HasValue) - { - app.UseMiddleware(_port, _timeout); - } - else - { - app.UseMiddleware(_path, _timeout); - } - - next(app); - }; - } - } -} \ No newline at end of file diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs deleted file mode 100644 index 467293137..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostBuilderExtension.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.AspNetCore.HealthChecks; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.AspNetCore.Hosting -{ - public static class HealthCheckWebHostBuilderExtension - { - public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10); - - public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port) - => UseHealthChecks(builder, port, DefaultTimeout); - - public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port, TimeSpan timeout) - { - Guard.ArgumentValid(port > 0 && port < 65536, nameof(port), "Port must be a value between 1 and 65535."); - Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span."); - - builder.ConfigureServices(services => - { - var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey); - builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}"); - - services.AddSingleton(new HealthCheckStartupFilter(port, timeout)); - }); - return builder; - } - - public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path) - => UseHealthChecks(builder, path, DefaultTimeout); - - public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path, TimeSpan timeout) - { - Guard.ArgumentNotNull(nameof(path), path); - // REVIEW: Is there a better URL path validator somewhere? - Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values."); - Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with '/'."); - Guard.ArgumentValid(timeout > TimeSpan.Zero, nameof(timeout), "Health check timeout must be a positive time span."); - - builder.ConfigureServices(services => services.AddSingleton(new HealthCheckStartupFilter(path, timeout))); - return builder; - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostExtensions.cs deleted file mode 100644 index 67448ff17..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/HealthCheckWebHostExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.HealthChecks; - -namespace Microsoft.AspNetCore.Hosting -{ - public static class HealthCheckWebHostExtensions - { - private const int DEFAULT_TIMEOUT_SECONDS = 300; - - public static void RunWhenHealthy(this IWebHost webHost) - { - webHost.RunWhenHealthy(TimeSpan.FromSeconds(DEFAULT_TIMEOUT_SECONDS)); - } - - public static void RunWhenHealthy(this IWebHost webHost, TimeSpan timeout) - { - var healthChecks = webHost.Services.GetService(typeof(IHealthCheckService)) as IHealthCheckService; - - var loops = 0; - do - { - var checkResult = healthChecks.CheckHealthAsync().Result; - if (checkResult.CheckStatus == CheckStatus.Healthy) - { - webHost.Run(); - break; - } - - System.Threading.Thread.Sleep(1000); - loops++; - - } while (loops < timeout.TotalSeconds); - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj b/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj deleted file mode 100644 index 63468882c..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.AspNetCore.HealthChecks/Microsoft.AspNetCore.HealthChecks.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard2.0 - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs deleted file mode 100644 index 5a06a3ba2..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/AzureHealthCheckBuilderExtensions.cs +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.WindowsAzure.Storage; -using Microsoft.WindowsAzure.Storage.Auth; - -namespace Microsoft.Extensions.HealthChecks -{ - // REVIEW: Do we want these to continue to use default parameters? - // REVIEW: What are the appropriate guards for these functions? - - public static class AzureHealthCheckBuilderExtensions - { - public static HealthCheckBuilder AddAzureBlobStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string containerName = null, TimeSpan? cacheDuration = null) - { - var credentials = new StorageCredentials(accountName, accountKey); - var storageAccount = new CloudStorageAccount(credentials, true); - return AddAzureBlobStorageCheck(builder, storageAccount, containerName, cacheDuration); - } - - public static HealthCheckBuilder AddAzureBlobStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string containerName = null, TimeSpan? cacheDuration = null) - { - builder.AddCheck($"AzureBlobStorageCheck {storageAccount.BlobStorageUri} {containerName}", async () => - { - bool result; - try - { - var blobClient = storageAccount.CreateCloudBlobClient(); - - var properties = await blobClient.GetServicePropertiesAsync().ConfigureAwait(false); - - if (!String.IsNullOrWhiteSpace(containerName)) - { - var container = blobClient.GetContainerReference(containerName); - - result = await container.ExistsAsync(); - } - - result = true; - } - catch (Exception) - { - result = false; - } - - return result - ? HealthCheckResult.Healthy($"AzureBlobStorage {storageAccount.BlobStorageUri} is available") - : HealthCheckResult.Unhealthy($"AzureBlobStorage {storageAccount.BlobStorageUri} is unavailable"); - }, cacheDuration ?? builder.DefaultCacheDuration); - - return builder; - } - - public static HealthCheckBuilder AddAzureTableStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string tableName = null, TimeSpan? cacheDuration = null) - { - var credentials = new StorageCredentials(accountName, accountKey); - var storageAccount = new CloudStorageAccount(credentials, true); - return AddAzureTableStorageCheck(builder, storageAccount, tableName, cacheDuration); - } - - public static HealthCheckBuilder AddAzureTableStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string tableName = null, TimeSpan? cacheDuration = null) - { - builder.AddCheck($"AzureTableStorageCheck {storageAccount.TableStorageUri} {tableName}", async () => - { - bool result; - try - { - var tableClient = storageAccount.CreateCloudTableClient(); - - var properties = await tableClient.GetServicePropertiesAsync().ConfigureAwait(false); - - if (!String.IsNullOrWhiteSpace(tableName)) - { - var table = tableClient.GetTableReference(tableName); - - result = await table.ExistsAsync(); - } - result = true; - } - catch (Exception) - { - result = false; - } - - return result - ? HealthCheckResult.Healthy($"AzureTableStorage {storageAccount.BlobStorageUri} is available") - : HealthCheckResult.Unhealthy($"AzureTableStorage {storageAccount.BlobStorageUri} is unavailable"); - - }, cacheDuration ?? builder.DefaultCacheDuration); - - return builder; - } - - public static HealthCheckBuilder AddAzureFileStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string shareName = null, TimeSpan? cacheDuration = null) - { - var credentials = new StorageCredentials(accountName, accountKey); - var storageAccount = new CloudStorageAccount(credentials, true); - return AddAzureFileStorageCheck(builder, storageAccount, shareName, cacheDuration); - } - - public static HealthCheckBuilder AddAzureFileStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string shareName = null, TimeSpan? cacheDuration = null) - { - builder.AddCheck($"AzureFileStorageCheck {storageAccount.FileStorageUri} {shareName}", async () => - { - bool result; - try - { - var fileClient = storageAccount.CreateCloudFileClient(); - - var properties = await fileClient.GetServicePropertiesAsync().ConfigureAwait(false); - - if (!String.IsNullOrWhiteSpace(shareName)) - { - var share = fileClient.GetShareReference(shareName); - - result = await share.ExistsAsync(); - } - - result = true; - } - catch (Exception) - { - result = false; - } - - return result - ? HealthCheckResult.Healthy($"AzureFileStorage {storageAccount.BlobStorageUri} is available") - : HealthCheckResult.Unhealthy($"AzureFileStorage {storageAccount.BlobStorageUri} is unavailable"); - }, cacheDuration ?? builder.DefaultCacheDuration); - - return builder; - } - - public static HealthCheckBuilder AddAzureQueueStorageCheck(this HealthCheckBuilder builder, string accountName, string accountKey, string queueName = null, TimeSpan? cacheDuration = null) - { - var credentials = new StorageCredentials(accountName, accountKey); - var storageAccount = new CloudStorageAccount(credentials, true); - return AddAzureQueueStorageCheck(builder, storageAccount, queueName, cacheDuration); - } - - public static HealthCheckBuilder AddAzureQueueStorageCheck(HealthCheckBuilder builder, CloudStorageAccount storageAccount, string queueName = null, TimeSpan? cacheDuration = null) - { - builder.AddCheck($"AzureQueueStorageCheck {storageAccount.QueueStorageUri} {queueName}", async () => - { - bool result; - try - { - var queueClient = storageAccount.CreateCloudQueueClient(); - - var properties = await queueClient.GetServicePropertiesAsync().ConfigureAwait(false); - - if (!String.IsNullOrWhiteSpace(queueName)) - { - var queue = queueClient.GetQueueReference(queueName); - - result = await queue.ExistsAsync(); - } - result = true; - } - catch (Exception) - { - result = false; - } - - return result - ? HealthCheckResult.Healthy($"AzureFileStorage {storageAccount.BlobStorageUri} is available") - : HealthCheckResult.Unhealthy($"AzureFileStorage {storageAccount.BlobStorageUri} is unavailable"); - - }, cacheDuration ?? builder.DefaultCacheDuration); - - return builder; - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Microsoft.Extensions.HealthChecks.AzureStorage.csproj b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Microsoft.Extensions.HealthChecks.AzureStorage.csproj deleted file mode 100644 index 5972d7d8a..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Microsoft.Extensions.HealthChecks.AzureStorage.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netstandard2.0 - false - false - false - - - - - - - - - - - - - - - - - - - diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Properties/AssemblyInfo.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Properties/AssemblyInfo.cs deleted file mode 100644 index ef88235f5..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.AzureStorage/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Reflection; -using System.Runtime.CompilerServices; -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("HealthChecks.Azure")] -[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("0c4158b7-7153-4d2e-abfa-4ce07d44f75f")] diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs deleted file mode 100644 index 1837d9638..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/HealthCheckBuilderSqlServerExtensions.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Data; -using System.Data.SqlClient; - -namespace Microsoft.Extensions.HealthChecks -{ - // REVIEW: What are the appropriate guards for these functions? - - public static class HealthCheckBuilderSqlServerExtensions - { - public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return AddSqlCheck(builder, name, connectionString, builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddSqlCheck(this HealthCheckBuilder builder, string name, string connectionString, TimeSpan cacheDuration) - { - builder.AddCheck($"SqlCheck({name})", async () => - { - try - { - //TODO: There is probably a much better way to do this. - using (var connection = new SqlConnection(connectionString)) - { - connection.Open(); - using (var command = connection.CreateCommand()) - { - command.CommandType = CommandType.Text; - command.CommandText = "SELECT 1"; - var result = (int)await command.ExecuteScalarAsync().ConfigureAwait(false); - if (result == 1) - { - return HealthCheckResult.Healthy($"SqlCheck({name}): Healthy"); - } - - return HealthCheckResult.Unhealthy($"SqlCheck({name}): Unhealthy"); - } - } - } - catch (Exception ex) - { - return HealthCheckResult.Unhealthy($"SqlCheck({name}): Exception during check: {ex.GetType().FullName}"); - } - }, cacheDuration); - - return builder; - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj deleted file mode 100644 index 8b75a5b62..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks.SqlServer/Microsoft.Extensions.HealthChecks.SqlServer.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard2.0 - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheck.cs deleted file mode 100644 index 39ed087eb..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheck.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.Extensions.HealthChecks -{ - public abstract class CachedHealthCheck - { - private static readonly TypeInfo HealthCheckTypeInfo = typeof(IHealthCheck).GetTypeInfo(); - - private volatile int _writerCount; - - public CachedHealthCheck(string name, TimeSpan cacheDuration) - { - Guard.ArgumentNotNullOrEmpty(nameof(name), name); - Guard.ArgumentValid(cacheDuration.TotalMilliseconds >= 0, nameof(cacheDuration), "Cache duration must be zero (disabled) or greater than zero."); - - Name = name; - CacheDuration = cacheDuration; - } - - public IHealthCheckResult CachedResult { get; internal set; } - - public TimeSpan CacheDuration { get; } - - public DateTimeOffset CacheExpiration { get; internal set; } - - public string Name { get; } - - protected virtual DateTimeOffset UtcNow => DateTimeOffset.UtcNow; - - protected abstract IHealthCheck Resolve(IServiceProvider serviceProvider); - - public async ValueTask RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken = default(CancellationToken)) - { - while (CacheExpiration <= UtcNow) - { - // Can't use a standard lock here because of async, so we'll use this flag to determine when we should write a value, - // and the waiters who aren't allowed to write will just spin wait for the new value. - if (Interlocked.Exchange(ref _writerCount, 1) != 0) - { - await Task.Delay(5, cancellationToken).ConfigureAwait(false); - continue; - } - - try - { - var check = Resolve(serviceProvider); - CachedResult = await check.CheckAsync(cancellationToken); - } - catch (OperationCanceledException) - { - CachedResult = HealthCheckResult.Unhealthy("The health check operation timed out"); - } - catch (Exception ex) - { - CachedResult = HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}"); - } - - CacheExpiration = UtcNow + CacheDuration; - _writerCount = 0; - break; - } - - return CachedResult; - } - - public static CachedHealthCheck FromHealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck) - { - Guard.ArgumentNotNull(nameof(healthCheck), healthCheck); - - return new TypeOrHealthCheck_HealthCheck(name, cacheDuration, healthCheck); - } - - public static CachedHealthCheck FromType(string name, TimeSpan cacheDuration, Type healthCheckType) - { - Guard.ArgumentNotNull(nameof(healthCheckType), healthCheckType); - Guard.ArgumentValid(HealthCheckTypeInfo.IsAssignableFrom(healthCheckType.GetTypeInfo()), nameof(healthCheckType), $"Health check must implement '{typeof(IHealthCheck).FullName}'."); - - return new TypeOrHealthCheck_Type(name, cacheDuration, healthCheckType); - } - - class TypeOrHealthCheck_HealthCheck : CachedHealthCheck - { - private readonly IHealthCheck _healthCheck; - - public TypeOrHealthCheck_HealthCheck(string name, TimeSpan cacheDuration, IHealthCheck healthCheck) : base(name, cacheDuration) - => _healthCheck = healthCheck; - - protected override IHealthCheck Resolve(IServiceProvider serviceProvider) => _healthCheck; - } - - class TypeOrHealthCheck_Type : CachedHealthCheck - { - private readonly Type _healthCheckType; - - public TypeOrHealthCheck_Type(string name, TimeSpan cacheDuration, Type healthCheckType) : base(name, cacheDuration) - => _healthCheckType = healthCheckType; - - protected override IHealthCheck Resolve(IServiceProvider serviceProvider) - => (IHealthCheck)serviceProvider.GetRequiredService(_healthCheckType); - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheckExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheckExtensions.cs deleted file mode 100644 index 2c3388709..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CachedHealthCheckExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.HealthChecks -{ - public static class CachedHealthCheckExtensions - { - public static ValueTask RunAsync(this CachedHealthCheck check, IServiceProvider serviceProvider) - { - Guard.ArgumentNotNull(nameof(check), check); - - return check.RunAsync(serviceProvider, CancellationToken.None); - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CheckStatus.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CheckStatus.cs deleted file mode 100644 index c5d1a093f..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CheckStatus.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.Extensions.HealthChecks -{ - public enum CheckStatus - { - Unknown, - Unhealthy, - Healthy, - Warning - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs deleted file mode 100644 index 5b7b49af0..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/AddCheck.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.HealthChecks -{ - public static partial class HealthCheckBuilderExtensions - { - // Lambda versions of AddCheck for Func/Func/Func - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func check) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func check) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromCheck(check), builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func check, TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration); - } - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func check, TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromCheck(check), cacheDuration); - } - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func> check) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func> check) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func> check, TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration); - } - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name, Func> check, TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromTaskCheck(check), cacheDuration); - } - - public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func> check) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func> check) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func> check, TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration); - } - - public static HealthCheckBuilder AddValueTaskCheck(this HealthCheckBuilder builder, string name, Func> check, TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, HealthCheck.FromValueTaskCheck(check), cacheDuration); - } - - // IHealthCheck versions of AddCheck - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string checkName, IHealthCheck check) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(checkName, check, builder.DefaultCacheDuration); - } - - // Type versions of AddCheck - - public static HealthCheckBuilder AddCheck(this HealthCheckBuilder builder, string name) where TCheck : class, IHealthCheck - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return builder.AddCheck(name, builder.DefaultCacheDuration); - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs deleted file mode 100644 index 4c958234e..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/NumericChecks.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.HealthChecks -{ - public static partial class HealthCheckBuilderExtensions - { - // Numeric checks - - public static HealthCheckBuilder AddMinValueCheck(this HealthCheckBuilder builder, string name, T minValue, Func currentValueFunc) where T : IComparable - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return AddMinValueCheck(builder, name, minValue, currentValueFunc, builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddMinValueCheck(this HealthCheckBuilder builder, string name, T minValue, Func currentValueFunc, TimeSpan cacheDuration) - where T : IComparable - { - Guard.ArgumentNotNull(nameof(builder), builder); - Guard.ArgumentNotNullOrEmpty(nameof(name), name); - Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc); - - builder.AddCheck(name, () => - { - var currentValue = currentValueFunc(); - var status = currentValue.CompareTo(minValue) >= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy; - return HealthCheckResult.FromStatus( - status, - $"min={minValue}, current={currentValue}", - new Dictionary { { "min", minValue }, { "current", currentValue } } - ); - }, cacheDuration); - - return builder; - } - - public static HealthCheckBuilder AddMaxValueCheck(this HealthCheckBuilder builder, string name, T maxValue, Func currentValueFunc) where T : IComparable - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return AddMaxValueCheck(builder, name, maxValue, currentValueFunc, builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddMaxValueCheck(this HealthCheckBuilder builder, string name, T maxValue, Func currentValueFunc, TimeSpan cacheDuration) - where T : IComparable - { - Guard.ArgumentNotNull(nameof(builder), builder); - Guard.ArgumentNotNullOrEmpty(nameof(name), name); - Guard.ArgumentNotNull(nameof(currentValueFunc), currentValueFunc); - - builder.AddCheck(name, () => - { - var currentValue = currentValueFunc(); - var status = currentValue.CompareTo(maxValue) <= 0 ? CheckStatus.Healthy : CheckStatus.Unhealthy; - return HealthCheckResult.FromStatus( - status, - $"max={maxValue}, current={currentValue}", - new Dictionary { { "max", maxValue }, { "current", currentValue } } - ); - }, cacheDuration); - - return builder; - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs deleted file mode 100644 index dbd9feff2..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/SystemChecks.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Diagnostics; - -namespace Microsoft.Extensions.HealthChecks -{ - public static partial class HealthCheckBuilderExtensions - { - // System checks - - public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize) - => AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64); - - public static HealthCheckBuilder AddPrivateMemorySizeCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration) - => AddMaxValueCheck(builder, $"PrivateMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().PrivateMemorySize64, cacheDuration); - - public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize) - => AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64); - - public static HealthCheckBuilder AddVirtualMemorySizeCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration) - => AddMaxValueCheck(builder, $"VirtualMemorySize({maxSize})", maxSize, () => Process.GetCurrentProcess().VirtualMemorySize64, cacheDuration); - - public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize) - => AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64); - - public static HealthCheckBuilder AddWorkingSetCheck(this HealthCheckBuilder builder, long maxSize, TimeSpan cacheDuration) - => AddMaxValueCheck(builder, $"WorkingSet({maxSize})", maxSize, () => Process.GetCurrentProcess().WorkingSet64, cacheDuration); - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs deleted file mode 100644 index 2a6cfe908..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Checks/UrlChecks.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.HealthChecks.Internal; - -namespace Microsoft.Extensions.HealthChecks -{ - public static partial class HealthCheckBuilderExtensions - { - // Default URL check - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return AddUrlCheck(builder, url, builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, TimeSpan cacheDuration) - => AddUrlCheck(builder, url, response => UrlChecker.DefaultUrlCheck(response), cacheDuration); - - // Func returning IHealthCheckResult - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func checkFunc) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, - Func checkFunc, - TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); - - return AddUrlCheck(builder, url, response => new ValueTask(checkFunc(response)), cacheDuration); - } - - // Func returning Task - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func> checkFunc) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, - Func> checkFunc, - TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); - - return AddUrlCheck(builder, url, response => new ValueTask(checkFunc(response)), cacheDuration); - } - - // Func returning ValueTask - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, Func> checkFunc) - { - Guard.ArgumentNotNull(nameof(builder), builder); - - return AddUrlCheck(builder, url, checkFunc, builder.DefaultCacheDuration); - } - - public static HealthCheckBuilder AddUrlCheck(this HealthCheckBuilder builder, string url, - Func> checkFunc, - TimeSpan cacheDuration) - { - Guard.ArgumentNotNull(nameof(builder), builder); - Guard.ArgumentNotNullOrEmpty(nameof(url), url); - Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); - - var urlCheck = new UrlChecker(checkFunc, url); - builder.AddCheck($"UrlCheck({url})", () => urlCheck.CheckAsync(), cacheDuration); - return builder; - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs deleted file mode 100644 index 6894ce85f..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/CompositeHealthCheckResult.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.Extensions.HealthChecks -{ - /// - /// Represents a composite health check result built from several results. - /// - public class CompositeHealthCheckResult : IHealthCheckResult - { - private static readonly IReadOnlyDictionary _emptyData = new Dictionary(); - private readonly CheckStatus _initialStatus; - private readonly CheckStatus _partiallyHealthyStatus; - private readonly Dictionary _results = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public CompositeHealthCheckResult(CheckStatus partiallyHealthyStatus = CheckStatus.Warning, - CheckStatus initialStatus = CheckStatus.Unknown) - { - _partiallyHealthyStatus = partiallyHealthyStatus; - _initialStatus = initialStatus; - } - - public CheckStatus CheckStatus - { - get - { - var checkStatuses = new HashSet(_results.Select(x => x.Value.CheckStatus)); - if (checkStatuses.Count == 0) - { - return _initialStatus; - } - if (checkStatuses.Count == 1) - { - return checkStatuses.First(); - } - if (checkStatuses.Contains(CheckStatus.Healthy)) - { - return _partiallyHealthyStatus; - } - - return CheckStatus.Unhealthy; - } - } - - public string Description => string.Join(Environment.NewLine, _results.Select(r => $"{r.Key}: {r.Value.Description}")); - - public IReadOnlyDictionary Data - { - get - { - var result = new Dictionary(); - - foreach (var kvp in _results) - result.Add(kvp.Key, kvp.Value.Data); - - return result; - } - } - - public IReadOnlyDictionary Results => _results; - - public void Add(string name, CheckStatus status, string description) - => Add(name, status, description, null); - - public void Add(string name, CheckStatus status, string description, Dictionary data) - { - Guard.ArgumentNotNullOrEmpty(nameof(name), name); - Guard.ArgumentValid(status != CheckStatus.Unknown, nameof(status), "Cannot add 'Unknown' status to composite health check result."); - Guard.ArgumentNotNullOrEmpty(nameof(description), description); - - _results.Add(name, HealthCheckResult.FromStatus(status, description, data)); - } - - public void Add(string name, IHealthCheckResult checkResult) - { - Guard.ArgumentNotNullOrEmpty(nameof(name), name); - Guard.ArgumentNotNull(nameof(checkResult), checkResult); - - _results.Add(name, checkResult); - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs deleted file mode 100644 index 5e1caa2ff..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheck.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.HealthChecks -{ - public class HealthCheck : IHealthCheck - { - protected HealthCheck(Func> check) - { - Guard.ArgumentNotNull(nameof(check), check); - - Check = check; - } - - protected Func> Check { get; } - - public ValueTask CheckAsync(CancellationToken cancellationToken = default(CancellationToken)) - => Check(cancellationToken); - - public static HealthCheck FromCheck(Func check) - => new HealthCheck(token => new ValueTask(check())); - - public static HealthCheck FromCheck(Func check) - => new HealthCheck(token => new ValueTask(check(token))); - - public static HealthCheck FromTaskCheck(Func> check) - => new HealthCheck(token => new ValueTask(check())); - - public static HealthCheck FromTaskCheck(Func> check) - => new HealthCheck(token => new ValueTask(check(token))); - - public static HealthCheck FromValueTaskCheck(Func> check) - => new HealthCheck(token => check()); - - public static HealthCheck FromValueTaskCheck(Func> check) - => new HealthCheck(check); - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs deleted file mode 100644 index 006e4a6ef..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckBuilder.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.HealthChecks -{ - public class HealthCheckBuilder - { - private readonly Dictionary _checksByName; - private readonly HealthCheckGroup _currentGroup; - private readonly Dictionary _groups; - - public HealthCheckBuilder() - { - _checksByName = new Dictionary(StringComparer.OrdinalIgnoreCase); - _currentGroup = new HealthCheckGroup(string.Empty, CheckStatus.Unhealthy); - _groups = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - [string.Empty] = _currentGroup - }; - - DefaultCacheDuration = TimeSpan.FromMinutes(5); - } - - /// - /// This constructor should only be used when creating a grouped health check builder. - /// - public HealthCheckBuilder(HealthCheckBuilder rootBuilder, HealthCheckGroup currentGroup) - { - Guard.ArgumentNotNull(nameof(rootBuilder), rootBuilder); - Guard.ArgumentNotNull(nameof(currentGroup), currentGroup); - - _checksByName = rootBuilder._checksByName; - _currentGroup = currentGroup; - _groups = rootBuilder._groups; - - DefaultCacheDuration = rootBuilder.DefaultCacheDuration; - } - - /// - /// Gets the registered checks, indexed by check name. - /// - public IReadOnlyDictionary ChecksByName => _checksByName; - - /// - /// Gets the current default cache duration used when registering checks. - /// - public TimeSpan DefaultCacheDuration { get; private set; } - - /// - /// Gets the registered groups, indexed by group name. The root group's name is . - /// - public IReadOnlyDictionary Groups => _groups; - - /// - /// Registers a health check type that will later be resolved via dependency - /// injection. - /// - public HealthCheckBuilder AddCheck(string checkName, TimeSpan cacheDuration) where TCheck : class, IHealthCheck - { - Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName); - Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered."); - - var namedCheck = CachedHealthCheck.FromType(checkName, cacheDuration, typeof(TCheck)); - - _checksByName.Add(checkName, namedCheck); - _currentGroup.ChecksInternal.Add(namedCheck); - - return this; - } - - /// - /// Registers a concrete health check to the builder. - /// - public HealthCheckBuilder AddCheck(string checkName, IHealthCheck check, TimeSpan cacheDuration) - { - Guard.ArgumentNotNullOrEmpty(nameof(checkName), checkName); - Guard.ArgumentNotNull(nameof(check), check); - Guard.ArgumentValid(!_checksByName.ContainsKey(checkName), nameof(checkName), $"A check with name '{checkName}' has already been registered."); - - var namedCheck = CachedHealthCheck.FromHealthCheck(checkName, cacheDuration, check); - - _checksByName.Add(checkName, namedCheck); - _currentGroup.ChecksInternal.Add(namedCheck); - - return this; - } - - /// - /// Creates a new health check group, to which you can add one or more health - /// checks. Uses when the group is - /// partially successful. - /// - public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action groupChecks) - => AddHealthCheckGroup(groupName, groupChecks, CheckStatus.Unhealthy); - - /// - /// Creates a new health check group, to which you can add one or more health - /// checks. - /// - public HealthCheckBuilder AddHealthCheckGroup(string groupName, Action groupChecks, CheckStatus partialSuccessStatus) - { - Guard.ArgumentNotNullOrEmpty(nameof(groupName), groupName); - Guard.ArgumentNotNull(nameof(groupChecks), groupChecks); - Guard.ArgumentValid(partialSuccessStatus != CheckStatus.Unknown, nameof(partialSuccessStatus), "Check status 'Unknown' is not valid for partial success."); - Guard.ArgumentValid(!_groups.ContainsKey(groupName), nameof(groupName), $"A group with name '{groupName}' has already been registered."); - Guard.OperationValid(_currentGroup.GroupName == string.Empty, "Nested groups are not supported by HealthCheckBuilder."); - - var group = new HealthCheckGroup(groupName, partialSuccessStatus); - _groups.Add(groupName, group); - - var innerBuilder = new HealthCheckBuilder(this, group); - groupChecks(innerBuilder); - - return this; - } - - public HealthCheckBuilder WithDefaultCacheDuration(TimeSpan duration) - { - Guard.ArgumentValid(duration >= TimeSpan.Zero, nameof(duration), "Duration must be zero (disabled) or a positive duration."); - - DefaultCacheDuration = duration; - return this; - } - - public HealthCheckBuilder WithPartialSuccessStatus(CheckStatus partiallyHealthyStatus) - { - _currentGroup.PartiallyHealthyStatus = partiallyHealthyStatus; - - return this; - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckGroup.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckGroup.cs deleted file mode 100644 index 18c55132b..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckGroup.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace Microsoft.Extensions.HealthChecks -{ - public class HealthCheckGroup - { - private CheckStatus _partialSuccessStatus; - - public HealthCheckGroup(string groupName, CheckStatus partialSuccessStatus) - { - Guard.ArgumentNotNull(nameof(groupName), groupName); - - GroupName = groupName; - PartiallyHealthyStatus = partialSuccessStatus; - } - - public IReadOnlyList Checks => ChecksInternal.AsReadOnly(); - - internal List ChecksInternal { get; } = new List(); - - public string GroupName { get; } - - public CheckStatus PartiallyHealthyStatus - { - get => _partialSuccessStatus; - internal set - { - Guard.ArgumentValid(value != CheckStatus.Unknown, nameof(value), "Check status 'Unknown' is not valid for partial success."); - - _partialSuccessStatus = value; - } - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs deleted file mode 100644 index d8ef80dc4..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResult.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace Microsoft.Extensions.HealthChecks -{ - public class HealthCheckResult : IHealthCheckResult - { - private static readonly IReadOnlyDictionary _emptyData = new Dictionary(); - - public CheckStatus CheckStatus { get; } - public IReadOnlyDictionary Data { get; } - public string Description { get; } - - private HealthCheckResult(CheckStatus checkStatus, string description, IReadOnlyDictionary data) - { - CheckStatus = checkStatus; - Description = description; - Data = data ?? _emptyData; - } - - public static HealthCheckResult Unhealthy(string description) - => new HealthCheckResult(CheckStatus.Unhealthy, description, null); - - public static HealthCheckResult Unhealthy(string description, IReadOnlyDictionary data) - => new HealthCheckResult(CheckStatus.Unhealthy, description, data); - - public static HealthCheckResult Healthy(string description) - => new HealthCheckResult(CheckStatus.Healthy, description, null); - - public static HealthCheckResult Healthy(string description, IReadOnlyDictionary data) - => new HealthCheckResult(CheckStatus.Healthy, description, data); - - public static HealthCheckResult Warning(string description) - => new HealthCheckResult(CheckStatus.Warning, description, null); - - public static HealthCheckResult Warning(string description, IReadOnlyDictionary data) - => new HealthCheckResult(CheckStatus.Warning, description, data); - - public static HealthCheckResult Unknown(string description) - => new HealthCheckResult(CheckStatus.Unknown, description, null); - - public static HealthCheckResult Unknown(string description, IReadOnlyDictionary data) - => new HealthCheckResult(CheckStatus.Unknown, description, data); - - public static HealthCheckResult FromStatus(CheckStatus status, string description) - => new HealthCheckResult(status, description, null); - - public static HealthCheckResult FromStatus(CheckStatus status, string description, IReadOnlyDictionary data) - => new HealthCheckResult(status, description, data); - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResults.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResults.cs deleted file mode 100644 index d85dd9e39..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckResults.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace Microsoft.Extensions.HealthChecks -{ - public class HealthCheckResults - { - public IList CheckResults { get; } = new List(); - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs deleted file mode 100644 index 1d2934e0e..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckService.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.Extensions.HealthChecks -{ - public class HealthCheckService : IHealthCheckService - { - private readonly HealthCheckBuilder _builder; - private readonly IReadOnlyList _groups; - private readonly HealthCheckGroup _root; - private readonly IServiceProvider _serviceProvider; - private readonly IServiceScopeFactory _serviceScopeFactory; - - public HealthCheckService(HealthCheckBuilder builder, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory) - { - _builder = builder; - _groups = GetGroups().Where(group => group.GroupName != string.Empty).ToList(); - _root = GetGroup(string.Empty); - _serviceProvider = serviceProvider; - _serviceScopeFactory = serviceScopeFactory; - } - - public async Task CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - using (var scope = GetServiceScope()) - { - var scopeServiceProvider = scope.ServiceProvider; - var groupTasks = _groups.Select(group => new { Group = group, Task = RunGroupAsync(scopeServiceProvider, group, cancellationToken) }).ToList(); - var result = await RunGroupAsync(scopeServiceProvider, _root, cancellationToken).ConfigureAwait(false); - - await Task.WhenAll(groupTasks.Select(x => x.Task)); - - foreach (var groupTask in groupTasks) - { - result.Add($"Group({groupTask.Group.GroupName})", groupTask.Task.Result); - } - - return result; - } - } - - public IReadOnlyList GetAllChecks() - => _builder.ChecksByName.Values.ToList().AsReadOnly(); - - public CachedHealthCheck GetCheck(string checkName) - => _builder.ChecksByName[checkName]; - - public HealthCheckGroup GetGroup(string groupName) - => _builder.Groups[groupName]; - - public IReadOnlyList GetGroups() - => _builder.Groups.Values.ToList().AsReadOnly(); - - private IServiceScope GetServiceScope() - => _serviceScopeFactory == null ? new UnscopedServiceProvider(_serviceProvider) : _serviceScopeFactory.CreateScope(); - - public async ValueTask RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var scope = GetServiceScope()) - { - return await RunCheckAsync(scope.ServiceProvider, healthCheck, cancellationToken).ConfigureAwait(false); - } - } - - /// - /// Uses the provided service provider and executes the provided check. - /// - public ValueTask RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)) - { - Guard.ArgumentNotNull(nameof(serviceProvider), serviceProvider); - Guard.ArgumentNotNull(nameof(healthCheck), healthCheck); - - return healthCheck.RunAsync(serviceProvider, cancellationToken); - } - - /// - /// Creates a new resolution scope from the default service provider and executes the checks in the given group. - /// - public async Task RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var scope = GetServiceScope()) - return await RunGroupAsync(scope.ServiceProvider, group, cancellationToken).ConfigureAwait(false); - } - - /// - /// Uses the provided service provider and executes the checks in the given group. - /// - public async Task RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)) - { - var result = new CompositeHealthCheckResult(group.PartiallyHealthyStatus); - var checkTasks = group.Checks.Select(check => new { Check = check, Task = check.RunAsync(serviceProvider, cancellationToken).AsTask() }).ToList(); - await Task.WhenAll(checkTasks.Select(checkTask => checkTask.Task)); - - foreach (var checkTask in checkTasks) - { - result.Add(checkTask.Check.Name, checkTask.Task.Result); - } - - return result; - } - - private class UnscopedServiceProvider : IServiceScope - { - public UnscopedServiceProvider(IServiceProvider serviceProvider) - => ServiceProvider = serviceProvider; - - public IServiceProvider ServiceProvider { get; } - - public void Dispose() { } - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs deleted file mode 100644 index 678731737..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/HealthCheckServiceCollectionExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using Microsoft.Extensions.HealthChecks; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class HealthCheckServiceCollectionExtensions - { - private static readonly Type HealthCheckServiceInterface = typeof(IHealthCheckService); - - public static IServiceCollection AddHealthChecks(this IServiceCollection services, Action checks) - { - Guard.OperationValid(!services.Any(descriptor => descriptor.ServiceType == HealthCheckServiceInterface), "AddHealthChecks may only be called once."); - - var builder = new HealthCheckBuilder(); - - services.AddSingleton(serviceProvider => - { - var serviceScopeFactory = serviceProvider.GetService(); - return new HealthCheckService(builder, serviceProvider, serviceScopeFactory); - }); - - checks(builder); - return services; - } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs deleted file mode 100644 index e4aa45d28..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheck.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.HealthChecks -{ - public interface IHealthCheck - { - ValueTask CheckAsync(CancellationToken cancellationToken = default(CancellationToken)); - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckResult.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckResult.cs deleted file mode 100644 index 18a97f93c..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckResult.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Collections.Generic; - -namespace Microsoft.Extensions.HealthChecks -{ - public interface IHealthCheckResult - { - CheckStatus CheckStatus { get; } - string Description { get; } - IReadOnlyDictionary Data { get; } - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs deleted file mode 100644 index 17a49cb00..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/IHealthCheckService.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.HealthChecks -{ - public interface IHealthCheckService - { - /// - /// Runs all registered health checks. - /// - Task CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Gets all registered health checks as a flat list. - /// - IReadOnlyList GetAllChecks(); - - /// - /// Gets a health check by name. - /// - CachedHealthCheck GetCheck(string checkName); - - /// - /// Gets all health checks in a group. - /// - HealthCheckGroup GetGroup(string groupName); - - /// - /// Gets all the health check groups. - /// - IReadOnlyList GetGroups(); - - /// - /// Creates a new resolution scope from the default service provider and executes the provided check. - /// - ValueTask RunCheckAsync(CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Uses the provided service provider and executes the provided check. - /// - ValueTask RunCheckAsync(IServiceProvider serviceProvider, CachedHealthCheck healthCheck, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Creates a new resolution scope from the default service provider and executes the checks in the given group. - /// - Task RunGroupAsync(HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Uses the provided service provider and executes the checks in the given group. - /// - Task RunGroupAsync(IServiceProvider serviceProvider, HealthCheckGroup group, CancellationToken cancellationToken = default(CancellationToken)); - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs deleted file mode 100644 index 56800d334..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Internal/UrlChecker.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.HealthChecks.Internal -{ - public class UrlChecker - { - private readonly Func> _checkFunc; - private readonly string _url; - - public UrlChecker(Func> checkFunc, string url) - { - Guard.ArgumentNotNull(nameof(checkFunc), checkFunc); - Guard.ArgumentNotNullOrEmpty(nameof(url), url); - - _checkFunc = checkFunc; - _url = url; - } - - public CheckStatus PartiallyHealthyStatus { get; set; } = CheckStatus.Warning; - - public async Task CheckAsync() - { - using (var httpClient = CreateHttpClient()) - { - try - { - var response = await httpClient.GetAsync(_url).ConfigureAwait(false); - return await _checkFunc(response); - } - catch (Exception ex) - { - var data = new Dictionary { { "url", _url } }; - return HealthCheckResult.Unhealthy($"Exception during check: {ex.GetType().FullName}", data); - } - } - } - - private HttpClient CreateHttpClient() - { - var httpClient = GetHttpClient(); - httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true }; - return httpClient; - } - - public static async ValueTask DefaultUrlCheck(HttpResponseMessage response) - { - var status = response.IsSuccessStatusCode ? CheckStatus.Healthy : CheckStatus.Unhealthy; - var data = new Dictionary - { - { "url", response.RequestMessage.RequestUri.ToString() }, - { "status", (int)response.StatusCode }, - { "reason", response.ReasonPhrase }, - { "body", await response.Content?.ReadAsStringAsync() } - }; - return HealthCheckResult.FromStatus(status, $"status code {response.StatusCode} ({(int)response.StatusCode})", data); - } - - protected virtual HttpClient GetHttpClient() - => new HttpClient(); - } -} diff --git a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj b/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj deleted file mode 100644 index 2b7deec7b..000000000 --- a/src/BuildingBlocks/HealthChecks/src/Microsoft.Extensions.HealthChecks/Microsoft.Extensions.HealthChecks.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netstandard2.0 - - - - - - - - - - - - - - - - diff --git a/src/BuildingBlocks/HealthChecks/src/common/Guard.cs b/src/BuildingBlocks/HealthChecks/src/common/Guard.cs deleted file mode 100644 index 9f394be51..000000000 --- a/src/BuildingBlocks/HealthChecks/src/common/Guard.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -static class Guard -{ - public static void ArgumentNotNull(string argumentName, object value) - { - if (value == null) - { - throw new ArgumentNullException(argumentName); - } - } - - public static void ArgumentNotNullOrEmpty(string argumentName, string value) - { - if (value == null) - { - throw new ArgumentNullException(argumentName); - } - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Value cannot be an empty string.", argumentName); - } - } - - // Use IReadOnlyCollection instead of IEnumerable to discourage double enumeration - public static void ArgumentNotNullOrEmpty(string argumentName, IReadOnlyCollection items) - { - if (items == null) - { - throw new ArgumentNullException(argumentName); - } - if (items.Count == 0) - { - throw new ArgumentException("Collection must contain at least one item.", argumentName); - } - } - - public static void ArgumentValid(bool valid, string argumentName, string exceptionMessage) - { - if (!valid) - { - throw new ArgumentException(exceptionMessage, argumentName); - } - } - - public static void OperationValid(bool valid, string exceptionMessage) - { - if (!valid) - { - throw new InvalidOperationException(exceptionMessage); - } - } -} diff --git a/src/Services/Basket/Basket.API/Basket.API.csproj b/src/Services/Basket/Basket.API/Basket.API.csproj index 1fc290a4e..f665a5513 100644 --- a/src/Services/Basket/Basket.API/Basket.API.csproj +++ b/src/Services/Basket/Basket.API/Basket.API.csproj @@ -13,6 +13,10 @@ + + + + @@ -21,16 +25,15 @@ - + + - - diff --git a/src/Services/Basket/Basket.API/Program.cs b/src/Services/Basket/Basket.API/Program.cs index 303a4625d..ce27f7ef0 100644 --- a/src/Services/Basket/Basket.API/Program.cs +++ b/src/Services/Basket/Basket.API/Program.cs @@ -22,7 +22,6 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API { options.ConfigPath = "/Failing"; }) - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() .ConfigureAppConfiguration((builderContext, config) => diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index cc100789d..416ed98f4 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -4,10 +4,13 @@ using Basket.API.Infrastructure.Filters; using Basket.API.Infrastructure.Middlewares; using Basket.API.IntegrationEvents.EventHandling; using Basket.API.IntegrationEvents.Events; +using HealthChecks.UI.Client; + using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -22,7 +25,6 @@ using Microsoft.eShopOnContainers.Services.Basket.API.Model; using Microsoft.eShopOnContainers.Services.Basket.API.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using RabbitMQ.Client; @@ -31,7 +33,6 @@ using Swashbuckle.AspNetCore.Swagger; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; -using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Basket.API { @@ -62,12 +63,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API ConfigureAuthService(services); - services.AddHealthChecks(checks => - { - checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask(HealthCheckResult.Healthy("Ok")), - TimeSpan.Zero //No cache for this HealthCheck, better just for demos - ); - }); + services.AddCustomHealthCheck(Configuration); services.Configure(Configuration); @@ -192,6 +188,11 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API app.UsePathBase(pathBase); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); @@ -312,6 +313,40 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API eventBus.Subscribe(); eventBus.Subscribe(); + } + } + + public static class CustomExtensionMethods + { + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + hcBuilder + .AddRedis( + configuration["ConnectionString"], + name: "redis-check", + tags: new string[] { "redis" }); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "basket-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "basket-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; } } } diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj index 4581a5ccc..64e428047 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -34,11 +34,17 @@ + + + + + + @@ -50,10 +56,6 @@ - - - - diff --git a/src/Services/Catalog/Catalog.API/Program.cs b/src/Services/Catalog/Catalog.API/Program.cs index 8f3910c84..6f94ac767 100644 --- a/src/Services/Catalog/Catalog.API/Program.cs +++ b/src/Services/Catalog/Catalog.API/Program.cs @@ -34,7 +34,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API WebHost.CreateDefaultBuilder(args) .UseStartup() .UseApplicationInsights() - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .UseWebRoot("Pics") .ConfigureAppConfiguration((builderContext, config) => diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 804ca1eeb..847d89580 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -22,13 +22,14 @@ using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHa using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using RabbitMQ.Client; using System; using System.Data.Common; using System.Reflection; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Microsoft.eShopOnContainers.Services.Catalog.API { @@ -49,7 +50,8 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API .AddCustomOptions(Configuration) .AddIntegrationServices(Configuration) .AddEventBus(Configuration) - .AddSwagger(); + .AddSwagger() + .AddCustomHealthCheck(Configuration); var container = new ContainerBuilder(); container.Populate(services); @@ -72,6 +74,12 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API app.UsePathBase(pathBase); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously @@ -120,24 +128,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API } public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration) - { - services.AddHealthChecks(checks => - { - var minutes = 1; - if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } - checks.AddSqlCheck("CatalogDb", configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); - - var accountName = configuration.GetValue("AzureStorageAccountName"); - var accountKey = configuration.GetValue("AzureStorageAccountKey"); - if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) - { - checks.AddAzureBlobStorageCheck(accountName, accountKey); - } - }); - + { services.AddMvc(options => { options.Filters.Add(typeof(HttpGlobalExceptionFilter)); @@ -157,6 +148,49 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API return services; } + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var accountName = configuration.GetValue("AzureStorageAccountName"); + var accountKey = configuration.GetValue("AzureStorageAccountKey"); + + var hcBuilder = services.AddHealthChecks(); + + hcBuilder + .AddSqlServer( + configuration["ConnectionString"], + name: "CatalogDB-check", + tags: new string[] { "catalogdb" }); + + if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) + { + hcBuilder + .AddAzureBlobStorage( + $"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey};EndpointSuffix=core.windows.net", + name: "catalog-storage-check", + tags: new string[] { "catalogstorage" }); + } + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "catalog-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "catalog-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) { services.AddDbContext(options => diff --git a/src/Services/Identity/Identity.API/Identity.API.csproj b/src/Services/Identity/Identity.API/Identity.API.csproj index 301f24f31..005049451 100644 --- a/src/Services/Identity/Identity.API/Identity.API.csproj +++ b/src/Services/Identity/Identity.API/Identity.API.csproj @@ -13,6 +13,9 @@ + + + @@ -41,9 +44,6 @@ - - - diff --git a/src/Services/Identity/Identity.API/Program.cs b/src/Services/Identity/Identity.API/Program.cs index 6b8353062..61311addb 100644 --- a/src/Services/Identity/Identity.API/Program.cs +++ b/src/Services/Identity/Identity.API/Program.cs @@ -40,7 +40,6 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseKestrel() - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup() diff --git a/src/Services/Identity/Identity.API/Startup.cs b/src/Services/Identity/Identity.API/Startup.cs index 049bac090..aa7000aa7 100644 --- a/src/Services/Identity/Identity.API/Startup.cs +++ b/src/Services/Identity/Identity.API/Startup.cs @@ -15,11 +15,12 @@ using Microsoft.eShopOnContainers.Services.Identity.API.Models; using Microsoft.eShopOnContainers.Services.Identity.API.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using StackExchange.Redis; using System; using System.Reflection; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Microsoft.eShopOnContainers.Services.Identity.API { @@ -65,16 +66,11 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API .PersistKeysToRedis(ConnectionMultiplexer.Connect(Configuration["DPConnectionString"]), "DataProtection-Keys"); } - services.AddHealthChecks(checks => - { - var minutes = 1; - if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } - checks.AddSqlCheck("Identity_Db", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); - }); - + services.AddHealthChecks() + .AddSqlServer(Configuration["ConnectionString"], + name: "IdentityDB-check", + tags: new string[] { "IdentityDB" }); + services.AddTransient, EFLoginService>(); services.AddTransient(); @@ -143,6 +139,12 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously diff --git a/src/Services/Location/Locations.API/Locations.API.csproj b/src/Services/Location/Locations.API/Locations.API.csproj index 60cc3f95b..a6c8ed324 100644 --- a/src/Services/Location/Locations.API/Locations.API.csproj +++ b/src/Services/Location/Locations.API/Locations.API.csproj @@ -6,11 +6,16 @@ aspnet-Locations.API-20161122013619 + + + + + @@ -24,8 +29,6 @@ - - diff --git a/src/Services/Location/Locations.API/Program.cs b/src/Services/Location/Locations.API/Program.cs index aedc1bed5..c63e20f9e 100644 --- a/src/Services/Location/Locations.API/Program.cs +++ b/src/Services/Location/Locations.API/Program.cs @@ -17,7 +17,6 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() .ConfigureAppConfiguration((builderContext, config) => diff --git a/src/Services/Location/Locations.API/Startup.cs b/src/Services/Location/Locations.API/Startup.cs index eaad3c921..b08bff883 100644 --- a/src/Services/Location/Locations.API/Startup.cs +++ b/src/Services/Location/Locations.API/Startup.cs @@ -1,9 +1,11 @@ using Autofac; using Autofac.Extensions.DependencyInjection; +using HealthChecks.UI.Client; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -19,14 +21,12 @@ using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Reposito using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using RabbitMQ.Client; using Swashbuckle.AspNetCore.Swagger; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; -using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Locations.API { @@ -43,7 +43,9 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API { RegisterAppInsights(services); - services.AddMvc(options => + services + .AddCustomHealthCheck(Configuration) + .AddMvc(options => { options.Filters.Add(typeof(HttpGlobalExceptionFilter)); }) @@ -95,12 +97,7 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); }); - } - - services.AddHealthChecks(checks => - { - checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask(HealthCheckResult.Healthy("Ok"))); - }); + } RegisterEventBus(services); @@ -169,6 +166,12 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + app.UseCors("CorsPolicy"); ConfigureAuth(app); @@ -272,4 +275,38 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API services.AddSingleton(); } } + + public static class CustomExtensionMethods + { + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + hcBuilder + .AddMongoDb( + configuration["ConnectionString"], + name: "locations-mongodb-check", + tags: new string[] { "mongodb" }); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "locations-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "locations-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + } } diff --git a/src/Services/Marketing/Marketing.API/Marketing.API.csproj b/src/Services/Marketing/Marketing.API/Marketing.API.csproj index e42d56074..390b3a994 100644 --- a/src/Services/Marketing/Marketing.API/Marketing.API.csproj +++ b/src/Services/Marketing/Marketing.API/Marketing.API.csproj @@ -20,10 +20,17 @@ + + + + + + + @@ -36,9 +43,6 @@ - - - diff --git a/src/Services/Marketing/Marketing.API/Program.cs b/src/Services/Marketing/Marketing.API/Program.cs index 06c5b72ca..f889e2780 100644 --- a/src/Services/Marketing/Marketing.API/Program.cs +++ b/src/Services/Marketing/Marketing.API/Program.cs @@ -28,7 +28,6 @@ public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseApplicationInsights() - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() .UseWebRoot("Pics") diff --git a/src/Services/Marketing/Marketing.API/Startup.cs b/src/Services/Marketing/Marketing.API/Startup.cs index 04fdc07a2..e43687a72 100644 --- a/src/Services/Marketing/Marketing.API/Startup.cs +++ b/src/Services/Marketing/Marketing.API/Startup.cs @@ -13,8 +13,8 @@ using EntityFrameworkCore; using Extensions.Configuration; using Extensions.DependencyInjection; - using Extensions.HealthChecks; using Extensions.Logging; + using HealthChecks.UI.Client; using Infrastructure; using Infrastructure.Filters; using Infrastructure.Repositories; @@ -24,6 +24,7 @@ using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Authentication.JwtBearer; + using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Middlewares; @@ -33,7 +34,6 @@ using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Reflection; - using System.Threading.Tasks; public class Startup { @@ -51,29 +51,19 @@ RegisterAppInsights(services); // Add framework services. - services.AddMvc(options => - { - options.Filters.Add(typeof(HttpGlobalExceptionFilter)); - }) - .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) - .AddControllersAsServices(); //Injecting Controllers themselves thru DIFor further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services + services + .AddCustomHealthCheck(Configuration) + .AddMvc(options => + { + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }) + .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) + .AddControllersAsServices(); //Injecting Controllers themselves thru DIFor further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services services.Configure(Configuration); ConfigureAuthService(services); - services.AddHealthChecks(checks => - { - checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask(HealthCheckResult.Healthy("Ok"))); - - var accountName = Configuration.GetValue("AzureStorageAccountName"); - var accountKey = Configuration.GetValue("AzureStorageAccountKey"); - if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) - { - checks.AddAzureBlobStorageCheck(accountName, accountKey); - } - }); - services.AddDbContext(options => { options.UseSqlServer(Configuration["ConnectionString"], @@ -197,6 +187,12 @@ app.UsePathBase(pathBase); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously @@ -310,4 +306,53 @@ app.UseAuthentication(); } } + + public static class CustomExtensionMethods + { + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + hcBuilder + .AddSqlServer( + configuration["ConnectionString"], + name: "MarketingDB-check", + tags: new string[] { "marketingdb" }) + .AddMongoDb( + configuration["MongoConnectionString"], + name: "MarketingDB-mongodb-check", + tags: new string[] { "mongodb" }); + + var accountName = configuration.GetValue("AzureStorageAccountName"); + var accountKey = configuration.GetValue("AzureStorageAccountKey"); + if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) + { + hcBuilder + .AddAzureBlobStorage( + $"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey};EndpointSuffix=core.windows.net", + name: "marketing-storage-check", + tags: new string[] { "marketingstorage" }); + } + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "marketing-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "marketing-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + } } diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index 10c368f34..750779750 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -20,16 +20,17 @@ - - - - + + + + + @@ -37,6 +38,7 @@ + diff --git a/src/Services/Ordering/Ordering.API/Program.cs b/src/Services/Ordering/Ordering.API/Program.cs index 92ef6ba86..f0d236300 100644 --- a/src/Services/Ordering/Ordering.API/Program.cs +++ b/src/Services/Ordering/Ordering.API/Program.cs @@ -34,7 +34,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .ConfigureAppConfiguration((builderContext, config) => { diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 59b8f0142..df93b2e44 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -26,7 +26,6 @@ using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using Ordering.Infrastructure; using RabbitMQ.Client; @@ -36,6 +35,8 @@ using System.Data.Common; using System.IdentityModel.Tokens.Jwt; using System.Reflection; + using HealthChecks.UI.Client; + using Microsoft.AspNetCore.Diagnostics.HealthChecks; public class Startup { @@ -88,6 +89,13 @@ app.UseCors("CorsPolicy"); + + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + ConfigureAuth(app); app.UseMvcWithDefaultRoute(); @@ -175,15 +183,31 @@ public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) { - services.AddHealthChecks(checks => + var hcBuilder = services.AddHealthChecks(); + + hcBuilder + .AddSqlServer( + configuration["ConnectionString"], + name: "OrderingDB-check", + tags: new string[] { "orderingdb" }); + + if (configuration.GetValue("AzureServiceBusEnabled")) { - var minutes = 1; - if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } - checks.AddSqlCheck("OrderingDb", configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); - }); + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "ordering-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "ordering-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } return services; } diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj index 8d2e7bfd2..25d2c1d80 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj @@ -14,18 +14,20 @@ + + + + + - - - diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs index 23d0a5e42..8112579f5 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs @@ -13,8 +13,7 @@ namespace Ordering.BackgroundTasks public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) - .UseStartup() - .UseHealthChecks("/hc") + .UseStartup() .ConfigureLogging((hostingContext, builder) => { builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs index 2bc085dbf..a0afc0ea8 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs @@ -9,13 +9,14 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Ordering.BackgroundTasks.Configuration; using Ordering.BackgroundTasks.Tasks; using RabbitMQ.Client; using System; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Ordering.BackgroundTasks { @@ -32,16 +33,7 @@ namespace Ordering.BackgroundTasks public IServiceProvider ConfigureServices(IServiceCollection services) { //add health check for this service - services.AddHealthChecks(checks => - { - var minutes = 1; - - if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } - checks.AddSqlCheck("OrderingDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); - }); + services.AddCustomHealthCheck(Configuration); //configure settings @@ -112,6 +104,11 @@ namespace Ordering.BackgroundTasks // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env) { + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); @@ -158,4 +155,38 @@ namespace Ordering.BackgroundTasks services.AddSingleton(); } } + + public static class CustomExtensionMethods + { + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + hcBuilder + .AddSqlServer( + configuration["ConnectionString"], + name: "OrderingTaskDB-check", + tags: new string[] { "orderingtaskdb" }); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "orderingtask-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "orderingtask-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj index c6b6835c9..2ec270e5d 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj +++ b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj @@ -10,8 +10,12 @@ + + + + diff --git a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs index 25a8b22d9..186ebf954 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs @@ -15,9 +15,10 @@ using Ordering.SignalrHub.IntegrationEvents; using Ordering.SignalrHub.IntegrationEvents.EventHandling; using Ordering.SignalrHub.IntegrationEvents.Events; using RabbitMQ.Client; -using StackExchange.Redis; using System; using System.IdentityModel.Tokens.Jwt; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Ordering.SignalrHub { @@ -32,14 +33,16 @@ namespace Ordering.SignalrHub // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public IServiceProvider ConfigureServices(IServiceCollection services) { - services.AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder.AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); - }); + services + .AddCustomHealthCheck(Configuration) + .AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); if (Configuration.GetValue("IsClusterEnv") == bool.TrueString) { @@ -127,6 +130,12 @@ namespace Ordering.SignalrHub app.UsePathBase(pathBase); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + app.UseCors("CorsPolicy"); app.UseAuthentication(); @@ -211,4 +220,32 @@ namespace Ordering.SignalrHub services.AddSingleton(); } } + + public static class CustomExtensionMethods + { + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "signalr-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "signalr-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + } } diff --git a/src/Services/Payment/Payment.API/Payment.API.csproj b/src/Services/Payment/Payment.API/Payment.API.csproj index d28979e5c..61d52ede4 100644 --- a/src/Services/Payment/Payment.API/Payment.API.csproj +++ b/src/Services/Payment/Payment.API/Payment.API.csproj @@ -7,6 +7,9 @@ + + + @@ -20,9 +23,6 @@ - - - diff --git a/src/Services/Payment/Payment.API/Program.cs b/src/Services/Payment/Payment.API/Program.cs index aff7bf359..2379b2c63 100644 --- a/src/Services/Payment/Payment.API/Program.cs +++ b/src/Services/Payment/Payment.API/Program.cs @@ -16,7 +16,6 @@ namespace Payment.API public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() .ConfigureAppConfiguration((builderContext, config) => diff --git a/src/Services/Payment/Payment.API/Startup.cs b/src/Services/Payment/Payment.API/Startup.cs index 02cd4c70e..7cfba1298 100644 --- a/src/Services/Payment/Payment.API/Startup.cs +++ b/src/Services/Payment/Payment.API/Startup.cs @@ -1,5 +1,7 @@ using Autofac; using Autofac.Extensions.DependencyInjection; +using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Azure.ServiceBus; @@ -9,15 +11,13 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using Payment.API.IntegrationEvents.EventHandling; using Payment.API.IntegrationEvents.Events; using RabbitMQ.Client; using System; -using System.Threading.Tasks; -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.ServiceFabric; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace Payment.API { @@ -32,7 +32,8 @@ namespace Payment.API // This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) - { + { + services.AddCustomHealthCheck(Configuration); services.Configure(Configuration); RegisterAppInsights(services); @@ -77,12 +78,7 @@ namespace Payment.API return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); }); - } - - services.AddHealthChecks(checks => - { - checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask(HealthCheckResult.Healthy("Ok"))); - }); + } RegisterEventBus(services); @@ -103,6 +99,13 @@ namespace Payment.API app.UsePathBase(pathBase); } + + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously @@ -174,4 +177,32 @@ namespace Payment.API eventBus.Subscribe(); } } + + public static class CustomExtensionMethods + { + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "payment-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "payment-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + } } \ No newline at end of file diff --git a/src/Web/WebMVC/Program.cs b/src/Web/WebMVC/Program.cs index 07332a02a..528ba66f7 100644 --- a/src/Web/WebMVC/Program.cs +++ b/src/Web/WebMVC/Program.cs @@ -16,7 +16,6 @@ namespace Microsoft.eShopOnContainers.WebMVC public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseContentRoot(Directory.GetCurrentDirectory()) - .UseHealthChecks("/hc") .UseStartup() .ConfigureAppConfiguration((builderContext, config) => { diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index c313f87fd..a3d2f9bb6 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -1,9 +1,11 @@ -using Microsoft.ApplicationInsights.Extensibility; +using HealthChecks.UI.Client; +using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -53,6 +55,12 @@ namespace Microsoft.eShopOnContainers.WebMVC loggerFactory.AddAzureWebAppDiagnostics(); loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); @@ -129,21 +137,11 @@ namespace Microsoft.eShopOnContainers.WebMVC public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) { - services.AddHealthChecks(checks => - { - var minutes = 1; - if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } - - checks.AddUrlCheck(configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos - checks.AddUrlCheck(configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes)); - }); - + services.AddHealthChecks() + .AddUrlGroup(new Uri(configuration["PurchaseUrlHC"]), name: "purchaseapigw-check", tags: new string[] { "purchaseapigw" }) + .AddUrlGroup(new Uri(configuration["MarketingUrlHC"]), name: "marketingapigw-check", tags: new string[] { "marketingapigw" }) + .AddUrlGroup(new Uri(configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }); + return services; } diff --git a/src/Web/WebMVC/WebMVC.csproj b/src/Web/WebMVC/WebMVC.csproj index ed9646724..be8f6d566 100644 --- a/src/Web/WebMVC/WebMVC.csproj +++ b/src/Web/WebMVC/WebMVC.csproj @@ -19,11 +19,15 @@ + + + + @@ -41,12 +45,6 @@ - - - - - - diff --git a/src/Web/WebSPA/Program.cs b/src/Web/WebSPA/Program.cs index c1a7da269..6660585b8 100644 --- a/src/Web/WebSPA/Program.cs +++ b/src/Web/WebSPA/Program.cs @@ -16,7 +16,6 @@ namespace eShopConContainers.WebSPA public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() - .UseHealthChecks("/hc") .UseContentRoot(Directory.GetCurrentDirectory()) .ConfigureAppConfiguration((builderContext, config) => { diff --git a/src/Web/WebSPA/Startup.cs b/src/Web/WebSPA/Startup.cs index c979dbafb..53bf2906c 100644 --- a/src/Web/WebSPA/Startup.cs +++ b/src/Web/WebSPA/Startup.cs @@ -8,13 +8,14 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Serialization; using StackExchange.Redis; using System; using System.IO; using WebSPA.Infrastructure; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; namespace eShopConContainers.WebSPA { @@ -42,21 +43,10 @@ namespace eShopConContainers.WebSPA { RegisterAppInsights(services); - services.AddHealthChecks(checks => - { - var minutes = 1; - if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } - - checks.AddUrlCheck(Configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(Configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(Configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos - checks.AddUrlCheck(Configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(Configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes)); - - }); + services.AddHealthChecks() + .AddUrlGroup(new Uri(Configuration["PurchaseUrlHC"]), name: "purchaseapigw-check", tags: new string[] { "purchaseapigw" }) + .AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapigw-check", tags: new string[] { "marketingapigw" }) + .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }); services.Configure(Configuration); @@ -97,6 +87,12 @@ namespace eShopConContainers.WebSPA app.UseHsts(); } + app.UseHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + // Configure XSRF middleware, This pattern is for SPA style applications where XSRF token is added on Index page // load and passed back token on every subsequent async request // app.Use(async (context, next) => diff --git a/src/Web/WebSPA/WebSPA.csproj b/src/Web/WebSPA/WebSPA.csproj index 88cf55cf2..f3408e697 100644 --- a/src/Web/WebSPA/WebSPA.csproj +++ b/src/Web/WebSPA/WebSPA.csproj @@ -85,6 +85,8 @@ + + @@ -105,11 +107,6 @@ --> - - - - - diff --git a/src/Web/WebStatus/Controllers/HomeController.cs b/src/Web/WebStatus/Controllers/HomeController.cs index 7f139843a..f2afab4b7 100644 --- a/src/Web/WebStatus/Controllers/HomeController.cs +++ b/src/Web/WebStatus/Controllers/HomeController.cs @@ -1,32 +1,17 @@ using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.HealthChecks; using System.Threading.Tasks; -using WebStatus.Viewmodels; namespace WebStatus.Controllers { public class HomeController : Controller { - private readonly IHealthCheckService _healthCheckSvc; - public HomeController(IHealthCheckService checkSvc) + public HomeController() { - _healthCheckSvc = checkSvc; } - public async Task Index() + public IActionResult Index() { - var result = await _healthCheckSvc.CheckHealthAsync(); - - var data = new HealthStatusViewModel(result.CheckStatus); - - foreach (var checkResult in result.Results) - { - data.AddResult(checkResult.Key, checkResult.Value); - } - - ViewBag.RefreshSeconds = 60; - - return View(data); + return Redirect("/hc-ui"); } public IActionResult Error() diff --git a/src/Web/WebStatus/Extensions/HealthCheckBuilderExtensions.cs b/src/Web/WebStatus/Extensions/HealthCheckBuilderExtensions.cs deleted file mode 100644 index a6076df52..000000000 --- a/src/Web/WebStatus/Extensions/HealthCheckBuilderExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.Extensions.HealthChecks; -using System; - -namespace WebStatus.Extensions -{ - public static class HealthCheckBuilderExtensions - { - public static HealthCheckBuilder AddUrlCheckIfNotNull(this HealthCheckBuilder builder, string url, TimeSpan cacheDuration) - { - if (!string.IsNullOrEmpty(url)) - { - builder.AddUrlCheck(url, cacheDuration); - } - - return builder; - } - } -} diff --git a/src/Web/WebStatus/Program.cs b/src/Web/WebStatus/Program.cs index adab0e6b3..d0e5a4296 100644 --- a/src/Web/WebStatus/Program.cs +++ b/src/Web/WebStatus/Program.cs @@ -16,11 +16,7 @@ namespace WebStatus public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseContentRoot(Directory.GetCurrentDirectory()) - .UseStartup() - .ConfigureAppConfiguration((builderContext, config) => - { - config.AddEnvironmentVariables(); - }) + .UseStartup() .ConfigureLogging((hostingContext, builder) => { builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); diff --git a/src/Web/WebStatus/Startup.cs b/src/Web/WebStatus/Startup.cs index b4a1049d8..c0eff980a 100644 --- a/src/Web/WebStatus/Startup.cs +++ b/src/Web/WebStatus/Startup.cs @@ -1,16 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using HealthChecks.UI.Client; +using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using WebStatus.Extensions; -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.ApplicationInsights.ServiceFabric; -using Microsoft.AspNetCore.Mvc; namespace WebStatus { @@ -29,28 +26,9 @@ namespace WebStatus RegisterAppInsights(services); services.AddOptions(); - - // Add framework services. - services.AddHealthChecks(checks => - { - var minutes = 1; - if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } - - checks.AddUrlCheckIfNotNull(Configuration["OrderingUrl"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheckIfNotNull(Configuration["OrderingBackgroundTasksUrl"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheckIfNotNull(Configuration["BasketUrl"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos - checks.AddUrlCheckIfNotNull(Configuration["CatalogUrl"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheckIfNotNull(Configuration["IdentityUrl"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheckIfNotNull(Configuration["LocationsUrl"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheckIfNotNull(Configuration["MarketingUrl"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheckIfNotNull(Configuration["PaymentUrl"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheckIfNotNull(Configuration["mvc"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos - checks.AddUrlCheckIfNotNull(Configuration["spa"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos - }); - + services.AddHealthChecks(); + services.AddHealthChecksUI(); + services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } @@ -82,6 +60,8 @@ namespace WebStatus app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + app.UseHealthChecksUI(config => config.UIPath = "/hc-ui"); + app.UseStaticFiles(); app.UseHttpsRedirection(); diff --git a/src/Web/WebStatus/Viewmodels/HealthStatusViewModel.cs b/src/Web/WebStatus/Viewmodels/HealthStatusViewModel.cs deleted file mode 100644 index 08126ce4f..000000000 --- a/src/Web/WebStatus/Viewmodels/HealthStatusViewModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.Extensions.HealthChecks; -using System.Collections.Generic; -using System.Linq; - -namespace WebStatus.Viewmodels -{ - public class HealthStatusViewModel - { - private readonly CheckStatus _overall; - - private readonly Dictionary _results; - - public CheckStatus OverallStatus => _overall; - - public IEnumerable Results => _results.Select(kvp => new NamedCheckResult(kvp.Key, kvp.Value)); - - private HealthStatusViewModel() => _results = new Dictionary(); - - public HealthStatusViewModel(CheckStatus overall) : this() => _overall = overall; - - public void AddResult(string name, IHealthCheckResult result) => _results.Add(name, result); - } -} diff --git a/src/Web/WebStatus/Viewmodels/NamedCheckResult.cs b/src/Web/WebStatus/Viewmodels/NamedCheckResult.cs deleted file mode 100644 index f6a9cd316..000000000 --- a/src/Web/WebStatus/Viewmodels/NamedCheckResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.Extensions.HealthChecks; - -namespace WebStatus.Viewmodels -{ - public class NamedCheckResult - { - public string Name { get; } - - public IHealthCheckResult Result { get; } - - public NamedCheckResult(string name, IHealthCheckResult result) - { - Name = name; - Result = result; - } - } -} diff --git a/src/Web/WebStatus/Views/Home/Index.cshtml b/src/Web/WebStatus/Views/Home/Index.cshtml deleted file mode 100644 index a3befe611..000000000 --- a/src/Web/WebStatus/Views/Home/Index.cshtml +++ /dev/null @@ -1,51 +0,0 @@ -@using Microsoft.AspNetCore.Html -@using Microsoft.Extensions.HealthChecks -@model WebStatus.Viewmodels.HealthStatusViewModel - -@{ - ViewData["Title"] = "System Status"; - -} - -@functions -{ - static readonly string[] LabelClass = new[] { "default", "danger", "success", "warning" }; - - public HtmlString StatusLabel(CheckStatus status) - { - return new HtmlString($@"{status}"); - } - -} - - - - -
-
-

Overall Status: @StatusLabel(Model.OverallStatus)

-
-
- -
- @foreach (var result in Model.Results) - { -
-
-

@result.Name

-

- @if (result.Result.Data.ContainsKey("url")) - { -

@result.Result.Data["url"]

- } -

- @result.Result.Description -

-

-
-
-

@StatusLabel(result.Result.CheckStatus)

-
-
- } -
\ No newline at end of file diff --git a/src/Web/WebStatus/Views/Shared/Error.cshtml b/src/Web/WebStatus/Views/Shared/Error.cshtml deleted file mode 100644 index e514139c4..000000000 --- a/src/Web/WebStatus/Views/Shared/Error.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. -

diff --git a/src/Web/WebStatus/Views/Shared/_Layout.cshtml b/src/Web/WebStatus/Views/Shared/_Layout.cshtml deleted file mode 100644 index b74f006e4..000000000 --- a/src/Web/WebStatus/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - @ViewData["Title"] - WebStatus - - - - - - - - - - - @if (ViewBag.RefreshSeconds != null && ViewBag.RefreshSeconds > 0) - { - - } - - - - -
-
- @RenderBody() -
-
- - - - - - - - - - - - - diff --git a/src/Web/WebStatus/Views/_ViewImports.cshtml b/src/Web/WebStatus/Views/_ViewImports.cshtml deleted file mode 100644 index 6a5648da4..000000000 --- a/src/Web/WebStatus/Views/_ViewImports.cshtml +++ /dev/null @@ -1,2 +0,0 @@ -@using WebStatus -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/Web/WebStatus/Views/_ViewStart.cshtml b/src/Web/WebStatus/Views/_ViewStart.cshtml deleted file mode 100644 index a5f10045d..000000000 --- a/src/Web/WebStatus/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "_Layout"; -} diff --git a/src/Web/WebStatus/WebStatus.csproj b/src/Web/WebStatus/WebStatus.csproj index e4dfaf8d4..56c9517f4 100644 --- a/src/Web/WebStatus/WebStatus.csproj +++ b/src/Web/WebStatus/WebStatus.csproj @@ -5,6 +5,9 @@ ..\..\..\docker-compose.dcproj + + + @@ -13,9 +16,4 @@ - - - - - diff --git a/src/Web/WebStatus/appsettings.json b/src/Web/WebStatus/appsettings.json index 2ab5ad818..6f486424c 100644 --- a/src/Web/WebStatus/appsettings.json +++ b/src/Web/WebStatus/appsettings.json @@ -1,21 +1,84 @@ { - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - }, - "OrderingUrl": "http://localhost:5102/hc", - "OrderingBackgroundTasksUrl": "http://localhost:5111/hc", - "BasketUrl": "http://localhost:5103/hc", - "CatalogUrl": "http://localhost:5101/hc", - "IdentityUrl": "http://localhost:5105/hc", - "MarketingUrl": "http://localhost:5110/hc", - "LocationsUrl": "http://localhost:5109/hc", - "PaymentUrl": "http://localhost:5108/hc", - "ApplicationInsights": { - "InstrumentationKey": "" + "HealthChecks-UI": { + "HealthChecks": [ + { + "Name": "Ordering HTTP Check", + "Uri": "http://localhost:5102/hc" + }, + { + "Name": "Ordering HTTP Background Check", + "Uri": "http://localhost:5111/hc" + }, + { + "Name": "Basket HTTP Check", + "Uri": "http://localhost:5103/hc" + }, + { + "Name": "Catalog HTTP Check", + "Uri": "http://localhost:5101/hc" + }, + { + "Name": "Identity HTTP Check", + "Uri": "http://localhost:5105/hc" + }, + { + "Name": "Marketing HTTP Check", + "Uri": "http://localhost:5110/hc" + }, + { + "Name": "Locations HTTP Check", + "Uri": "http://localhost:5109/hc" + }, + { + "Name": "Payments HTTP Check", + "Uri": "http://localhost:5108/hc" + }, + { + "Name": "WebMVC HTTP Check", + "Uri": "http://localhost:5100/hc" + }, + { + "Name": "WebSPA HTTP Check", + "Uri": "http://localhost:5104/hc" + }, + { + "Name": "SignalR HTTP Check", + "Uri": "http://localhost:5112/hc" + }, + { + "Name": "Mobile Shopping API GW HTTP Check", + "Uri": "http://localhost:5200/hc" + }, + { + "Name": "Mobile Marketing API GW HTTP Check", + "Uri": "http://localhost:5201/hc" + }, + { + "Name": "Web Shopping API GW HTTP Check", + "Uri": "http://localhost:5202/hc" + }, + { + "Name": "Web Marketing API GW HTTP Check", + "Uri": "http://localhost:5203/hc" + }, + { + "Name": "Mobile Shopping Aggregator HTTP Check", + "Uri": "http://localhost:5120/hc" + }, + { + "Name": "Web Shopping Aggregator HTTP Check", + "Uri": "http://localhost:5121/hc" + } + ], + "Webhooks": [ + { + "Name": "", + "Uri": "", + "Payload": "", + "RestoredPayload": "" + } + ], + "EvaluationTimeOnSeconds": 10, + "MinimumSecondsBetweenFailureNotifications": 60 } }