diff --git a/.dockerignore b/.dockerignore
index e58ae957a..dd3d41423 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -28,9 +28,7 @@ cli-linux
**/obj/
**/node_modules/
**/bower_components/
-**/wwwroot/lib/
-!**/wwwroot/lib/signalr/*
-!**/wwwroot/lib/toastr/*
+**/wwwroot/lib/*
global.json
**/appsettings.localhost.json
src/Web/WebSPA/wwwroot/
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 3c690a758..69800e3b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,10 @@ bld/
# Visual Studio 2015 cache/options directory
.vs/
+
+# .js files created on build:
+src/Web/WebMVC/wwwroot/js/site*
+
# Uncomment if you have tasks that create the project's static files in wwwroot
**/wwwroot/lib/
!/wwwroot/lib/signalr
@@ -263,3 +267,6 @@ pub/
/src/Web/WebMVC/wwwroot/css/site.min.css
**/.kube/**
.mfractor
+
+# Ignore HealthCheckdb
+*healthchecksdb*
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..e0ff17a32
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,67 @@
+# How to contribute to eShopOnContainers
+
+This repo is a reference and learning resource and everyone is invited to contribute, however not all PRs will be accepted into the main branch (**`dev`**).
+
+There's a general development strategy that's driven by @CESARDELATORRE, who chooses, or defines criteria for choosing, the issues to include in the codebase, given a bunch of constraints and other guidelines.
+
+However you can always get in touch with him, if you want to implement some general-interest feature in your repo and have it referenced from the [documentation](https://docs.microsoft.com/dotnet/standard/microservices-architecture/) or the [Microservices eBook](https://aka.ms/microservicesebook/).
+
+## Coding Standards
+
+There are no explicit coding standards so pay attention to the general coding style, that's (mostly) used everywhere.
+
+However, there's only one **REALLY** important rule: **use spaces for indenting** 😉.
+
+## Development Process
+
+In order to help manage community contributions and avoid conflicts, there's a [Development project](https://github.com/dotnet-architecture/eShopOnContainers/projects/3) in this repo, to track assignments to any significant development effort.
+
+Great but... **what's "significant"**?
+
+That's not too easy to define and there are no clear criteria right now but, probably, changing "a couple" lines of code in one file would not qualify while changing "a bunch" of files would.
+
+We'll all be learning in the process so we'll figure it out somehow.
+
+### General Steps
+
+1. Issues are managed as usual with the regular issues list, just like any other repo.
+
+2. Once an issue is marked as a bug, enhancement, new feature or whatever needs development work, it will be labeled as a **backlog-item** and included as the last item in the Backlog project column.
+
+3. Community members can propose themselves to code an issue.
+
+4. @CESARDELATORRE/collaborators will prioritize the backlog items and arrange them in the **Backlog** column, so that the items in the top of the list are implemented first.
+
+5. @CESARDELATORRE/collaborators will review the issues and select the ones approved to begin development with, and move them to the **Approved** column.
+
+6. Issues in the **Approved** column can be assigned to a **collaborator** or to a **community member** who would then begin working on the issue and submit a PR as usual.
+
+## Tests
+
+There's not a tests policy in the project at this moment, but it'll be greatly appreciated if you include them within the [updated test structure](./test/readme.md).
+
+## Forks and Branches
+
+All contributions must be submitted as a [Pull Request (PR)](https://help.github.com/articles/about-pull-requests/) so you need to [fork this repo](https://help.github.com/articles/fork-a-repo/) on your GitHub account.
+
+The main branches are **`dev`** and **`master`**:
+
+- **`dev`**: Contains the latest code **and it is the branch actively developed**.
+**All PRs must be against `dev` branch to be considered**. This branch is developed using .NET Core 2.x
+
+- **`master`**: Synced from time to time from **`dev`**. It contains "stable" code.
+(**Keep in mind "stable" does not mean PRODUCTION-READY!**)
+
+- Any other branch is considered temporary and could be deleted at any time. Do not submit any PR to them!
+
+## DISCLAIMER - This is not a PRODUCTION-READY TEMPLATE for microservices
+eShopOnContainers is a reference application to **showcase architectural patterns** for developing microservices applications on .NET Core. **IT IS NOT A PRODUCTION-READY TEMPLATE** to start real-world application. In fact, the application is in a **permanent beta state**, as it’s also used to test new potentially interesting technologies as they show up.
+
+Since this is a learning resource, some design decisions have favored simplicity to convey a pattern, over production-grade robustness.
+
+## Suggestions
+
+We hope this helps us all to work better and avoid some of the problems/frustrations of working in such a large community.
+
+We'd also appreciate any comments or ideas to improve this.
+
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/README.md b/README.md
index 4a4e1d3c3..ffdf6450a 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,31 @@
# eShopOnContainers - Microservices Architecture and Containers based Reference Application (**BETA state** - Visual Studio 2017 and CLI environments compatible)
Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers.
+[](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=184)
+
+
## IMPORTANT NOTES!
-**The current supported Visual Studio version for eShopOnContainers is Visual Studio 2017 15.7** ([GA/RTM since May 8th 2018](https://docs.microsoft.com/en-us/visualstudio/releasenotes/vs2017-relnotes)) or later version.
+**You can use either the latest version of Visual Studio or simply Docker CLI and .NET CLI for Windows, Mac and Linux**.
**Note for Pull Requests (PRs)**: We accept pull request from the community. When doing it, please do it onto the **DEV branch** which is the consolidated work-in-progress branch. Do not request it onto Master branch, if possible.
**NEWS / ANNOUNCEMENTS**
Do you want to be up-to-date on .NET Architecture guidance and reference apps like eShopOnContainers? --> Subscribe by "WATCHING" this new GitHub repo: https://github.com/dotnet-architecture/News
-## Updated for .NET Core 2.0 and 2.1 "wave" of technologies
-eShopOnContainers is updated to .NET Core 2.0 and 2.1 "wave". Not just compilation but also new recommended code in EF Core, ASP.NET Core, and other new related versions.
+## Updated for .NET Core 2.2 "wave" of technologies
+eShopOnContainers is updated to .NET Core 2.x (currently updated to 2.2) "wave" of technologies. Not just compilation but also new recommended code in EF Core, ASP.NET Core, and other new related versions.
The **dockerfiles** in the solution have also been updated and now support [**Docker Multi-Stage**](https://blogs.msdn.microsoft.com/stevelasker/2017/09/11/net-and-multistage-dockerfiles/) since mid-December 2017.
>**PLEASE** Read our [branch guide](./branch-guide.md) to know about our branching policy
> ### DISCLAIMER
-> **IMPORTANT:** The current state of this sample application is **BETA**, because we are constantly evolving towards new released technologies. Therefore, many areas could be improved and change significantly while refactoring current code and implementing new features. Feedback with improvements and pull requests from the community will be highly appreciated and accepted.
+> **IMPORTANT:** The current state of this sample application is **BETA**, because we are constantly evolving towards newly released technologies. Therefore, many areas could be improved and change significantly while refactoring the current code and implementing new features. Feedback with improvements and pull requests from the community will be highly appreciated and accepted.
>
-> This reference application proposes a simplified microservice oriented architecture implementation to introduce technologies like .NET Core with Docker containers through a comprehensive application. The chosen domain is an eShop/eCommerce but simply because it is a well-know domain by most people/developers.
-However, this sample application should not be considered as an "eCommerce reference model", at all. The implemented business domain might not be ideal from an eCommerce business point of view. It is neither trying to solve all the problems in a large, scalable and mission-critical distributed system. It is just a bootstrap for developers to easily get started in the world of Docker containers and microservices with .NET Core.
+> This reference application proposes a simplified microservice oriented architecture implementation to introduce technologies like .NET Core with Docker containers through a comprehensive application. The chosen domain is eShop/eCommerce but simply because it is a well-known domain by most people/developers.
+However, this sample application should not be considered as an "eCommerce reference model" at all. The implemented business domain might not be ideal from an eCommerce business point of view. It is neither trying to solve all the problems in a large, scalable and mission-critical distributed system. It is just a bootstrap for developers to easily get started in the world of Docker containers and microservices with .NET Core.
>
For example, the next step after running the solution in the local dev PC and understanding Docker containers and microservices development with .NET Core, is to select a microservice cluster/orchestrator like Kubernetes in Azure (AKS) or Azure Service Fabric, both environments tested and supported by this solution.
-> Additional steps would be to move your databases to HA cloud services (like Azure SQL Database), or switch your EventBus to use Azure Service Bus (instead of bare-bone RabbitMQ) or any other production ready Service Bus in the market.
+> Additional steps would be to move your databases to HA cloud services (like Azure SQL Database) or switch your EventBus to use Azure Service Bus (instead of bare-bone RabbitMQ) or any other production-ready Service Bus in the market.

@@ -39,7 +42,7 @@ The architecture proposes a microservice oriented architecture implementation wi
> ### Important Note on API Gateways and published APIs
> Since April 2018, we have introduced the implementation of the [API Gateway pattern](http://microservices.io/patterns/apigateway.html) and [Backend-For-Front-End (BFF) pattern](https://samnewman.io/patterns/architectural/bff/) in eShopOnContainers architecture, so you can filter and publish simplified APIs and URIs and apply additional security in that tier while hiding/securing the internal microservices to the client apps or outside consumers. These sample API Gateways in eShopOnContainers are based on [Ocelot](https://github.com/ThreeMammals/Ocelot), an OSS lightweight API Gateway solution explained [here](http://threemammals.com/ocelot). The deployed API Gateways are autonomous and can be deployed as your own custom microservices/containers, as it is currently done in eShopOnContainers, so you can test it even in a simple development environment with just Docker engine or deploy it into orchestrators like Kubernetes in AKS or Service Fabric.
-> For your production-ready architecture you can either keep using [Ocelot](https://github.com/ThreeMammals/Ocelot) which is simple and easy to use and used in production by significant companies or if you need further functionality and a much richer set of features suittable for commercial APIs, you can also substitute those API Gateways and use [Azure API Management](https://azure.microsoft.com/en-us/services/api-management/) or any other commercial API Gateway, as shown in the following image.
+> For your production-ready architecture you can either keep using [Ocelot](https://github.com/ThreeMammals/Ocelot) which is simple and easy to use and used in production by significant companies or if you need further functionality and a much richer set of features suitable for commercial APIs, you can also substitute those API Gateways and use [Azure API Management](https://azure.microsoft.com/en-us/services/api-management/) or any other commercial API Gateway, as shown in the following image.
@@ -56,9 +59,9 @@ The architecture proposes a microservice oriented architecture implementation wi
> ### Important Note on Database Servers/Containers
-> In this solution's current configuration for a development environment, the SQL databases are automatically deployed with sample data into a single SQL Server container (a single shared Docker container for SQL databases) so the whole solution can be up and running without any dependency to any cloud or specific server. Each database could also be deployed as a single Docker container, but then you'd need more than 8GB of RAM assigned to Docker in your development machine in order to be able to run 3 SQL Server Docker containers in your Docker Linux host in "Docker for Windows" or "Docker for Mac" development environments.
+> In this solution's current configuration for a development environment, the SQL databases are automatically deployed with sample data into a single SQL Server container (a single shared Docker container for SQL databases) so the whole solution can be up and running without any dependency to any cloud or a specific server. Each database could also be deployed as a single Docker container, but then you'd need more than 8GB of RAM assigned to Docker in your development machine in order to be able to run 3 SQL Server Docker containers in your Docker Linux host in "Docker for Windows" or "Docker for Mac" development environments.
>
A similar case is defined in regard to Redis cache running as a container for the development environment. Or a No-SQL database (MongoDB) running as a container.
->
However, in a real production environment it is recommended to have your databases (SQL Server, Redis, and the NO-SQL database, in this case) in HA (High Available) services like Azure SQL Database, Redis as a service and Azure CosmosDB instead the MongoDB container (as both systems share the same access protocol). If you want to change to a production configuration, you'll just need to change the connection strings once you have set up the servers in a HA cloud or on-premises.
+>
However, in a real production environment it is recommended to have your databases (SQL Server, Redis, and the NO-SQL database, in this case) in HA (High Available) services like Azure SQL Database, Redis as a service and Azure CosmosDB instead the MongoDB container (as both systems share the same access protocol). If you want to change to a production configuration, you'll just need to change the connection strings once you have set up the servers in an HA cloud or on-premises.
## Related documentation and guidance
While developing this reference application, we've been creating a reference Guide/eBook focusing on architecting and developing containerized and microservice based .NET Applications (download link available below) which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic apps that can also live as Docker containers.
@@ -70,18 +73,18 @@ You can download them and start reviewing these Guides/eBooks here:
| Architecting & Developing | Containers Lifecycle & CI/CD | App patterns with Xamarin.Forms |
| ------------ | ------------| ------------|
|
|
|
|
-| **Download .PDF** (v2.1 Edition) | **Download** | **Download** |
+| **Download .PDF** (v2.2 Edition) | **Download** | **Download** |
Download in other formats (**eReaders** like **MOBI**, **EPUB**) and other eBooks at the [.NET Architecture center](http://dot.net/architecture).
Send feedback to [dotnet-architecture-ebooks-feedback@service.microsoft.com](dotnet-architecture-ebooks-feedback@service.microsoft.com)
-However, we encourage to download and review the [Architecting and Developing Microservices eBook](https://aka.ms/microservicesebook) because the architectural styles and architectural patterns and technologies explained in the guidance are using this reference application when explaining many pattern implementations, so you'll understand much better the context, design and decisions taken in the current architecture and internal designs.
+However, we encourage you to download and review the [Architecting and Developing Microservices eBook](https://aka.ms/microservicesebook) because the architectural styles and architectural patterns and technologies explained in the guide are using this reference application when explaining many pattern implementations, so you'll understand the context, design and decisions taken in the current architecture and internal designs much better.
## Overview of the application code
In this repo you can find a sample reference application that will help you to understand how to implement a microservice architecture based application using .NET Core and Docker.
-The example business domain or scenario is based on an eShop or eCommerce which is implemented as a multi-container application. Each container is a microservice deployment (like the basket-microservice, catalog-microservice, ordering-microservice and the identity-microservice) which are developed using ASP.NET Core running on .NET Core so they can run either on Linux Containers and Windows Containers.
+The example business domain or scenario is based on an eShop or eCommerce which is implemented as a multi-container application. Each container is a microservice deployment (like the basket-microservice, catalog-microservice, ordering-microservice and the identity-microservice) which is developed using ASP.NET Core running on .NET Core so they can run either on Linux Containers and Windows Containers.
The screenshot below shows the VS Solution structure for those microservices/containers and client apps.
- (*Recommended when getting started*) Open eShopOnContainers-ServicesAndWebApps.sln for a solution containing just the server-side projects related to the microservices and web applications.
diff --git a/deploy/az/servicebus/readme.md b/deploy/az/servicebus/readme.md
index 16da4c7b2..886b3ec60 100644
--- a/deploy/az/servicebus/readme.md
+++ b/deploy/az/servicebus/readme.md
@@ -8,7 +8,7 @@ The ARM template `sbusdeploy.json` and its parameter file (`sbusdeploy.parameter
## Editing sbusdeploy.parameters.json file
-You can edit the `sbusdeploy.parameters.parameters.json` file to set your values, but is not needed. The only parameter than can
+You can edit the `sbusdeploy.parameters.json` file to set your values, but is not needed. The only parameter than can
be set is:
1. `namespaceprefix` is a string that is used to create the namespace. ARM script creates unique values by appending a unique string to this parameter value, so you can leave the default value.
@@ -21,4 +21,4 @@ i. e. if you are in windows, to deploy servicebus in a new resourcegroup located
```
create-resources.cmd servicebus\sbusdeploy newResourceGroup -c westus
-```
\ No newline at end of file
+```
diff --git a/docker-compose-tests.override.yml b/docker-compose-tests.override.yml
new file mode 100644
index 000000000..8705e703f
--- /dev/null
+++ b/docker-compose-tests.override.yml
@@ -0,0 +1,247 @@
+version: '3.4'
+
+services:
+ rabbitmq-test:
+ ports:
+ - "15672:15672"
+ - "5672:5672"
+
+ sql-data-test:
+ environment:
+ - SA_PASSWORD=Pass@word
+ - ACCEPT_EULA=Y
+ ports:
+ - "5433:1433"
+
+ nosql-data-test:
+ ports:
+ - "27017:27017"
+
+ basket-data-test:
+ ports:
+ - "6379:6379"
+
+ identity-api-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104
+ - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback
+ - ConnectionString=${ESHOP_AZURE_IDENTITY_DB:-Server=sql-data-test;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word}
+ - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100
+ - LocationApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5109
+ - MarketingApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110
+ - BasketApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
+ - OrderingApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
+ - MobileShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5120
+ - WebShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5121
+ - UseCustomizationData=True
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ ports:
+ - "5105:80"
+
+ basket-api-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_REDIS_BASKET_DB:-basket-data-test}
+ - identityUrl=http://identity-api
+ - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - AzureServiceBusEnabled=False
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5103:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/basket-test-results.xml
+
+ basket-api-unit-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_REDIS_BASKET_DB:-basket-data-test}
+ - identityUrl=http://identity-api
+ - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - AzureServiceBusEnabled=False
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5113:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/basket-unit-test-results.xml
+
+ catalog-api-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql-data-test;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word}
+ - PicBaseUrl=${ESHOP_AZURE_STORAGE_CATALOG_URL:-http://localhost:5202/api/v1/c/catalog/items/[0]/pic/}
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - AzureStorageAccountName=${ESHOP_AZURE_STORAGE_CATALOG_NAME}
+ - AzureStorageAccountKey=${ESHOP_AZURE_STORAGE_CATALOG_KEY}
+ - UseCustomizationData=True
+ - AzureServiceBusEnabled=False
+ - AzureStorageEnabled=False
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ ports:
+ - "5101:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/catalog-test-results.xml
+
+ catalog-api-unit-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql-data-test;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word}
+ - PicBaseUrl=${ESHOP_AZURE_STORAGE_CATALOG_URL:-http://localhost:5202/api/v1/c/catalog/items/[0]/pic/}
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - AzureStorageAccountName=${ESHOP_AZURE_STORAGE_CATALOG_NAME}
+ - AzureStorageAccountKey=${ESHOP_AZURE_STORAGE_CATALOG_KEY}
+ - UseCustomizationData=True
+ - AzureServiceBusEnabled=False
+ - AzureStorageEnabled=False
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ ports:
+ - "5191:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/catalog-unit-test-results.xml
+
+ ordering-api-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql-data-test;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word}
+ - identityUrl=http://identity-api
+ - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - UseCustomizationData=True
+ - AzureServiceBusEnabled=False
+ - CheckUpdateTime=30000
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5102:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/ordering-test-results.xml
+
+ ordering-api-unit-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql-data-test;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word}
+ - identityUrl=http://identity-api
+ - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - UseCustomizationData=True
+ - AzureServiceBusEnabled=False
+ - CheckUpdateTime=30000
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5112:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/ordering-unit-test-results.xml
+
+ marketing-api-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_MARKETING_DB:-Server=sql-data-test;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word}
+ - MongoConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql-data-test}
+ - MongoDatabase=MarketingDb
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - identityUrl=http://identity-api
+ - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
+ - CampaignDetailFunctionUri=${ESHOP_AZUREFUNC_CAMPAIGN_DETAILS_URI}
+ - PicBaseUrl=${ESHOP_AZURE_STORAGE_MARKETING_URL:-http://localhost:5110/api/v1/campaigns/[0]/pic/}
+ - AzureStorageAccountName=${ESHOP_AZURE_STORAGE_MARKETING_NAME}
+ - AzureStorageAccountKey=${ESHOP_AZURE_STORAGE_MARKETING_KEY}
+ - AzureServiceBusEnabled=False
+ - AzureStorageEnabled=False
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5110:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/marketing-test-results.xml
+
+ payment-api-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - AzureServiceBusEnabled=False
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ ports:
+ - "5108:80"
+
+ locations-api-test:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql-data-test}
+ - Database=LocationsDb
+ - identityUrl=http://identity-api
+ - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq-test}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - AzureServiceBusEnabled=False
+ - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
+ - OrchestratorType=${ORCHESTRATOR_TYPE}
+ - UseLoadTest=${USE_LOADTEST:-False}
+ ports:
+ - "5109:80"
+ entrypoint:
+ - dotnet
+ - test
+ - --logger
+ - trx;LogFileName=/tests/locations-test-results.xml
\ No newline at end of file
diff --git a/docker-compose-tests.yml b/docker-compose-tests.yml
new file mode 100644
index 000000000..7794d1a0e
--- /dev/null
+++ b/docker-compose-tests.yml
@@ -0,0 +1,130 @@
+version: '3.4'
+
+services:
+ rabbitmq-test:
+ image: rabbitmq:3-management-alpine
+
+ basket-data-test:
+ image: redis:alpine
+
+ sql-data-test:
+ image: microsoft/mssql-server-linux:2017-latest
+
+ nosql-data-test:
+ image: mongo
+
+ identity-api-test:
+ image: ${REGISTRY:-eshop}/identity-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Identity/Identity.API/Dockerfile
+ depends_on:
+ - sql-data-test
+
+ basket-api-test:
+ image: ${REGISTRY:-eshop}/basket-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Basket/Basket.API/Dockerfile
+ target: functionaltest
+ depends_on:
+ - basket-data-test
+ - identity-api-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
+
+ basket-api-unit-test:
+ image: ${REGISTRY:-eshop}/basket-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Basket/Basket.API/Dockerfile
+ target: unittest
+ depends_on:
+ - basket-data-test
+ - identity-api-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
+
+ catalog-api-test:
+ image: ${REGISTRY:-eshop}/catalog-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
+ target: functionaltest
+ depends_on:
+ - sql-data-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
+
+ catalog-api-unit-test:
+ image: ${REGISTRY:-eshop}/catalog-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
+ target: unittest
+ depends_on:
+ - sql-data-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
+
+ ordering-api-test:
+ image: ${REGISTRY:-eshop}/ordering-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Ordering/Ordering.API/Dockerfile
+ target: functionaltest
+ depends_on:
+ - sql-data-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
+
+ ordering-api-unit-test:
+ image: ${REGISTRY:-eshop}/ordering-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Ordering/Ordering.API/Dockerfile
+ target: unittest
+ depends_on:
+ - sql-data-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
+
+ marketing-api-test:
+ image: ${REGISTRY:-eshop}/marketing-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Marketing/Marketing.API/Dockerfile
+ target: functionaltest
+ depends_on:
+ - sql-data-test
+ - nosql-data-test
+ - identity-api-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
+
+ payment-api-test:
+ image: ${REGISTRY:-eshop}/payment-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Payment/Payment.API/Dockerfile
+ depends_on:
+ - rabbitmq-test
+
+ locations-api-test:
+ image: ${REGISTRY:-eshop}/locations-api-test:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Location/Locations.API/Dockerfile
+ target: functionaltest
+ depends_on:
+ - nosql-data-test
+ - rabbitmq-test
+ volumes:
+ - ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
\ No newline at end of file
diff --git a/docker-compose.override.yml b/docker-compose.override.yml
index 969cfb922..7353c744c 100644
--- a/docker-compose.override.yml
+++ b/docker-compose.override.yml
@@ -7,6 +7,12 @@ version: '3.4'
# An external IP or DNS name has to be used (instead localhost and the 10.0.75.1 IP) when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance.
services:
+ seq:
+ environment:
+ - ACCEPT_EULA=Y
+ ports:
+ - "5340:80"
+
sql.data:
environment:
- SA_PASSWORD=Pass@word
@@ -41,6 +47,8 @@ services:
- OrderingApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
- MobileShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5120
- WebShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5121
+ - WebhooksApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5113
+ - WebhooksWebClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5114
- UseCustomizationData=True
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
@@ -182,11 +190,35 @@ services:
ports:
- "5109:80" # Important: In a production environment your should remove the external port (5109) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
+ webhooks.api:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionString=${ESHOP_AZURE_WEBHOOKS_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.WebhooksDb;User Id=sa;Password=Pass@word}
+ - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq}
+ - EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
+ - EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
+ - IdentityUrl=http://identity.api
+ - IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
+ ports:
+ - "5113:80" # Important: In a production environment your should remove the external port (5109) kept here for microservice debugging purposes.
+ # The API Gateway redirects and access through the internal port (80).
mobileshoppingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
+ - urls__basket=http://basket.api
+ - urls__catalog=http://catalog.api
+ - urls__orders=http://ordering.api
+ - urls__identity=http://identity.api
+ - 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 +228,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 +244,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 +260,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 +279,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 +297,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).
@@ -259,16 +326,41 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- - CatalogUrl=http://catalog.api/hc
- - OrderingUrl=http://ordering.api/hc
+ - HealthChecks-UI__HealthChecks__1__Name=WebMVC HTTP Check
+ - HealthChecks-UI__HealthChecks__1__Uri=http://webmvc/hc
+ - HealthChecks-UI__HealthChecks__2__Name=WebSPA HTTP Check
+ - HealthChecks-UI__HealthChecks__2__Uri=http://webspa/hc
+ - HealthChecks-UI__HealthChecks__3__Name=Web Shopping Aggregator GW HTTP Check
+ - HealthChecks-UI__HealthChecks__3__Uri=http://webshoppingagg/hc
+ - HealthChecks-UI__HealthChecks__4__Name=Mobile Shopping Aggregator HTTP Check
+ - HealthChecks-UI__HealthChecks__4__Uri=http://mobileshoppingagg/hc
+ - HealthChecks-UI__HealthChecks__5__Name=Mobile Shopping API GW HTTP Check
+ - HealthChecks-UI__HealthChecks__5__Uri=http://mobileshoppingapigw/hc
+ - HealthChecks-UI__HealthChecks__6__Name=Mobile Marketing API GW HTTP Check
+ - HealthChecks-UI__HealthChecks__6__Uri=http://mobilemarketingapigw/hc
+ - HealthChecks-UI__HealthChecks__7__Name=Web Shopping API GW HTTP Check
+ - HealthChecks-UI__HealthChecks__7__Uri=http://webshoppingapigw/hc
+ - HealthChecks-UI__HealthChecks__8__Name=Web Marketing API GW HTTP Check
+ - HealthChecks-UI__HealthChecks__8__Uri=http://webmarketingapigw/hc
+ - HealthChecks-UI__HealthChecks__9__Name=Ordering HTTP Check
+ - HealthChecks-UI__HealthChecks__9__Uri=http://ordering.api/hc
+ - HealthChecks-UI__HealthChecks__10__Name=Ordering HTTP Background Check
+ - HealthChecks-UI__HealthChecks__10__Uri=http://ordering.backgroundtasks/hc
+ - HealthChecks-UI__HealthChecks__11__Name=Basket HTTP Check
+ - HealthChecks-UI__HealthChecks__11__Uri=http://basket.api/hc
+ - HealthChecks-UI__HealthChecks__12__Name=Catalog HTTP Check
+ - HealthChecks-UI__HealthChecks__12__Uri=http://catalog.api/hc
+ - HealthChecks-UI__HealthChecks__13__Name=Identity HTTP Check
+ - HealthChecks-UI__HealthChecks__13__Uri=http://identity.api/hc
+ - HealthChecks-UI__HealthChecks__14__Name=Marketing HTTP Check
+ - HealthChecks-UI__HealthChecks__14__Uri=http://marketing.api/hc
+ - HealthChecks-UI__HealthChecks__15__Name=Locations HTTP Check
+ - HealthChecks-UI__HealthChecks__15__Uri=http://locations.api/hc
+ - HealthChecks-UI__HealthChecks__16__Name=Payments HTTP Check
+ - HealthChecks-UI__HealthChecks__16__Uri=http://payment.api/hc
+ - HealthChecks-UI__HealthChecks__17__Name=Ordering SignalRHub HTTP Check
+ - HealthChecks-UI__HealthChecks__17__Uri=http://ordering.signalrhub/hc
- OrderingBackgroundTasksUrl=http://ordering.backgroundtasks/hc
- - BasketUrl=http://basket.api/hc
- - IdentityUrl=http://identity.api/hc
- - LocationsUrl=http://locations.api/hc
- - MarketingUrl=http://marketing.api/hc
- - PaymentUrl=http://payment.api/hc
- - mvc=http://webmvc/hc
- - spa=http://webspa/hc
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
@@ -281,12 +373,9 @@ services:
- IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- PurchaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5202
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5203
- - 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
+ - PurchaseUrlHC=http://webshoppingapigw/hc
+ - MarketingUrlHC=http://webmarketingapigw/hc
+ - IdentityUrlHC=http://identity.api/hc
- UseCustomizationData=True
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
@@ -300,14 +389,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}
@@ -315,3 +401,13 @@ services:
ports:
- "5100:80"
+ webhooks.client:
+ environment:
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - Token=6168DB8D-DC58-4094-AF24-483278923590 # Webhooks are registered with this token (any value is valid) but the client won't check it
+ - IdentityUrl=http://10.0.75.1:5105
+ - CallBackUrl=http://localhost:5114
+ - WebhooksUrl=http://webhooks.api
+ - SelfUrl=http://webhooks.client/
+ ports:
+ - "5114:80"
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 95610ebd5..55d5b10e6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,9 @@
version: '3.4'
services:
+ seq:
+ image: datalust/seq:latest
+
sql.data:
image: microsoft/mssql-server-linux:2017-latest
@@ -14,7 +17,7 @@ services:
image: rabbitmq:3-management-alpine
identity.api:
- image: eshop/identity.api:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/identity.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Identity/Identity.API/Dockerfile
@@ -22,7 +25,7 @@ services:
- sql.data
basket.api:
- image: eshop/basket.api:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/basket.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Basket/Basket.API/Dockerfile
@@ -32,7 +35,7 @@ services:
- rabbitmq
catalog.api:
- image: eshop/catalog.api:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/catalog.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
@@ -41,7 +44,7 @@ services:
- rabbitmq
ordering.api:
- image: eshop/ordering.api:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/ordering.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.API/Dockerfile
@@ -50,7 +53,7 @@ services:
- rabbitmq
ordering.backgroundtasks:
- image: eshop/ordering.backgroundtasks:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/ordering.backgroundtasks:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile
@@ -59,7 +62,7 @@ services:
- rabbitmq
marketing.api:
- image: eshop/marketing.api:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/marketing.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Marketing/Marketing.API/Dockerfile
@@ -70,7 +73,7 @@ services:
- rabbitmq
payment.api:
- image: eshop/payment.api:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/payment.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Payment/Payment.API/Dockerfile
@@ -78,7 +81,7 @@ services:
- rabbitmq
locations.api:
- image: eshop/locations.api:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/locations.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Location/Locations.API/Dockerfile
@@ -86,8 +89,16 @@ services:
- nosql.data
- rabbitmq
+ webhooks.api:
+ image: ${REGISTRY:-eshop}/webhooks.api:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Services/Webhooks/Webhooks.API/Dockerfile
+ depends_on:
+ - sql.data
+
mobileshoppingapigw:
- image: eshop/ocelotapigw:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@@ -102,7 +113,7 @@ services:
- basket.api
mobilemarketingapigw:
- image: eshop/ocelotapigw:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@@ -117,7 +128,7 @@ services:
- basket.api
webshoppingapigw:
- image: eshop/ocelotapigw:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@@ -132,7 +143,7 @@ services:
- basket.api
webmarketingapigw:
- image: eshop/ocelotapigw:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@@ -147,7 +158,7 @@ services:
- basket.api
mobileshoppingagg:
- image: eshop/mobileshoppingagg:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/mobileshoppingagg:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
@@ -162,7 +173,7 @@ services:
- basket.api
webshoppingagg:
- image: eshop/webshoppingagg:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/webshoppingagg:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile
@@ -177,7 +188,7 @@ services:
- basket.api
ordering.signalrhub:
- image: eshop/ordering.signalrhub:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/ordering.signalrhub:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.SignalrHub/Dockerfile
@@ -192,13 +203,13 @@ services:
- basket.api
webstatus:
- image: eshop/webstatus:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/webstatus:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebStatus/Dockerfile
webspa:
- image: eshop/webspa:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/webspa:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebSPA/Dockerfile
@@ -208,7 +219,7 @@ services:
# - webmarketingapigw
webmvc:
- image: eshop/webmvc:${TAG:-latest}
+ image: ${REGISTRY:-eshop}/webmvc:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebMVC/Dockerfile
@@ -217,3 +228,10 @@ services:
- webshoppingapigw
- webmarketingapigw
+ webhooks.client:
+ image: ${REGISTRY:-eshop}/webhooks.client:${TAG:-latest}
+ build:
+ context: .
+ dockerfile: src/Web/WebhookClient/Dockerfile
+ depends_on:
+ - webhooks.api
\ No newline at end of file
diff --git a/docs/Decks/BRK3175_CesarDeIaTorre.pptx b/docs/Decks/BRK3175_CesarDeIaTorre.pptx
new file mode 100644
index 000000000..e04d7338e
Binary files /dev/null and b/docs/Decks/BRK3175_CesarDeIaTorre.pptx differ
diff --git a/docs/Decks/eShopOnContainers-Architecture-v2.1.pptx b/docs/Decks/eShopOnContainers-Architecture-v2.1.pptx
new file mode 100644
index 000000000..52398f823
Binary files /dev/null and b/docs/Decks/eShopOnContainers-Architecture-v2.1.pptx differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.02.pdf b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.02.pdf
new file mode 100644
index 000000000..0bd88ca3e
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.02.pdf differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.03.pdf b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.03.pdf
new file mode 100644
index 000000000..0540236c9
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.1.03.pdf differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook).pdf b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook).pdf
index 0bd88ca3e..826a08f3b 100644
Binary files a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook).pdf and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook).pdf differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.01.mobi b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.01.mobi
new file mode 100644
index 000000000..b41f2adab
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.01.mobi differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.02.mobi b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.02.mobi
new file mode 100644
index 000000000..38ba1c9b1
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.02.mobi differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.03.mobi b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.03.mobi
new file mode 100755
index 000000000..5b9530e5c
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle)-v2.1.03.mobi differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle).mobi b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle).mobi
old mode 100644
new mode 100755
index b41f2adab..334f7f2e6
Binary files a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle).mobi and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-Kindle).mobi differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.01.epub b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.01.epub
new file mode 100644
index 000000000..6a0b776da
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.01.epub differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.02.epub b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.02.epub
new file mode 100644
index 000000000..15e2ca8b9
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.02.epub differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.03.epub b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.03.epub
new file mode 100755
index 000000000..48053f7ea
Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader)-v2.1.03.epub differ
diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader).epub b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader).epub
old mode 100644
new mode 100755
index 6a0b776da..7e093026d
Binary files a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader).epub and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-for-eReader).epub differ
diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln
index 583b4499c..447b73073 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}"
@@ -150,6 +140,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{C61C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Webhooks", "Webhooks", "{E0AA11C4-2873-461D-8F82-53392530FB7A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webhooks.API", "src\Services\Webhooks\Webhooks.API\Webhooks.API.csproj", "{84E2016E-0435-44C6-8020-3D288AA38B2C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebhookClient", "src\Web\WebhookClient\WebhookClient.csproj", "{766D7E92-6AF0-476C-ADD5-282BF4D8C576}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -644,54 +640,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 +688,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 +832,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
@@ -1844,6 +1648,102 @@ Global
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|x64.Build.0 = Release|Any CPU
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|x86.ActiveCfg = Release|Any CPU
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|x86.Build.0 = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x64.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x86.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|ARM.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x64.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x86.Build.0 = Debug|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|ARM.ActiveCfg = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|ARM.Build.0 = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhone.Build.0 = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x64.ActiveCfg = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x64.Build.0 = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x86.ActiveCfg = Release|Any CPU
+ {84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x86.Build.0 = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x64.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x86.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|ARM.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x64.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x86.Build.0 = Debug|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|Any CPU.Build.0 = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|ARM.ActiveCfg = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|ARM.Build.0 = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhone.Build.0 = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x64.ActiveCfg = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x64.Build.0 = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x86.ActiveCfg = Release|Any CPU
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1869,17 +1769,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}
@@ -1910,6 +1805,9 @@ Global
{120CABB3-0FEA-4B40-B4B5-2D3041798C80} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B}
{C61C5CFE-4876-4A46-A96E-5BBF596A984A} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84} = {C61C5CFE-4876-4A46-A96E-5BBF596A984A}
+ {E0AA11C4-2873-461D-8F82-53392530FB7A} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
+ {84E2016E-0435-44C6-8020-3D288AA38B2C} = {E0AA11C4-2873-461D-8F82-53392530FB7A}
+ {766D7E92-6AF0-476C-ADD5-282BF4D8C576} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9}
diff --git a/global.json b/global.json
deleted file mode 100644
index 079ebc941..000000000
--- a/global.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "sdk": {
- "version": "2.1.401"
- }
-}
diff --git a/k8s/deploy-ingress-azure.ps1 b/k8s/deploy-ingress-azure.ps1
index f93cf437b..d0ff702df 100644
--- a/k8s/deploy-ingress-azure.ps1
+++ b/k8s/deploy-ingress-azure.ps1
@@ -1,3 +1 @@
-kubectl patch deployment -n ingress-nginx nginx-ingress-controller --type=json --patch="$(cat nginx-ingress\publish-service-patch.yaml)"
-kubectl apply -f nginx-ingress\azure\service.yaml
-kubectl apply -f nginx-ingress\patch-service-without-rbac.yaml
\ No newline at end of file
+kubectl apply -f nginx-ingress\cloud-generic.yaml
\ No newline at end of file
diff --git a/k8s/deploy-ingress-dockerlocal.ps1 b/k8s/deploy-ingress-dockerlocal.ps1
new file mode 100644
index 000000000..04ffad763
--- /dev/null
+++ b/k8s/deploy-ingress-dockerlocal.ps1
@@ -0,0 +1,2 @@
+kubectl apply -f nginx-ingress\cm.yaml
+kubectl apply -f nginx-ingress\cloud-generic.yaml
\ No newline at end of file
diff --git a/k8s/deploy-ingress.ps1 b/k8s/deploy-ingress.ps1
index 694361bfa..37abcbee2 100644
--- a/k8s/deploy-ingress.ps1
+++ b/k8s/deploy-ingress.ps1
@@ -1,12 +1,5 @@
-kubectl apply -f ingress.yaml
-
# Deploy nginx-ingress core files
-kubectl apply -f nginx-ingress\namespace.yaml
-kubectl apply -f nginx-ingress\default-backend.yaml
-kubectl apply -f nginx-ingress\configmap.yaml
-kubectl apply -f nginx-ingress\tcp-services-configmap.yaml
-kubectl apply -f nginx-ingress\udp-services-configmap.yaml
-kubectl apply -f nginx-ingress\without-rbac.yaml
+kubectl apply -f nginx-ingress\mandatory.yaml
diff --git a/k8s/deploy.ps1 b/k8s/deploy.ps1
index f0905096a..abeb12aed 100644
--- a/k8s/deploy.ps1
+++ b/k8s/deploy.ps1
@@ -113,6 +113,7 @@ ExecKube -cmd 'delete configmap internalurls'
ExecKube -cmd 'delete configmap urls'
ExecKube -cmd 'delete configmap externalcfg'
ExecKube -cmd 'delete configmap ocelot'
+ExecKube -cmd 'delete -f ingress.yaml'
# start sql, rabbitmq, frontend deployments
if ($deployInfrastructure) {
@@ -204,5 +205,8 @@ ExecKube -cmd 'rollout resume deployments/apigwwm'
ExecKube -cmd 'rollout resume deployments/apigwws'
ExecKube -cmd 'rollout resume deployments/ordering-signalrhub'
+Write-Host "Adding/Updating ingress resource..." -ForegroundColor Yellow
+ExecKube -cmd 'apply -f ingress.yaml'
+
Write-Host "WebSPA is exposed at http://$externalDns, WebMVC at http://$externalDns/webmvc, WebStatus at http://$externalDns/webstatus" -ForegroundColor Yellow
diff --git a/k8s/helm-rbac.yaml b/k8s/helm-rbac.yaml
new file mode 100644
index 000000000..b6180329a
--- /dev/null
+++ b/k8s/helm-rbac.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: tiller
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: tiller
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+ - kind: ServiceAccount
+ name: tiller
+ namespace: kube-system
\ No newline at end of file
diff --git a/k8s/helm/apigwmm/templates/configmap.yaml b/k8s/helm/apigwmm/templates/configmap.yaml
index 2bd39aff5..fbffcd339 100644
--- a/k8s/helm/apigwmm/templates/configmap.yaml
+++ b/k8s/helm/apigwmm/templates/configmap.yaml
@@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
+ internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
+ internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
+ internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
+ internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
+ internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc
diff --git a/k8s/helm/apigwmm/templates/deployment.yaml b/k8s/helm/apigwmm/templates/deployment.yaml
index 2e30c78e7..6dc58bf50 100644
--- a/k8s/helm/apigwmm/templates/deployment.yaml
+++ b/k8s/helm/apigwmm/templates/deployment.yaml
@@ -36,6 +36,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:
diff --git a/k8s/helm/apigwmm/values.yaml b/k8s/helm/apigwmm/values.yaml
index 0bfca0ab5..501266780 100644
--- a/k8s/helm/apigwmm/values.yaml
+++ b/k8s/helm/apigwmm/values.yaml
@@ -32,8 +32,33 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
+ - name: CatalogUrlHC
+ key: internalurls__catalog__hc
+ - name: BasketUrlHC
+ key: internalurls__basket__hc
+ - name: IdentityUrlHC
+ key: internalurls__identity__hc
+ - name: OrderingUrlHC
+ key: internalurls__ordering__hc
+ - name: MarketingUrlHC
+ key: internalurls__marketing__hc
+ - name: PaymentUrlHC
+ key: internalurls__payment__hc
+ - name: LocationUrlHC
+ key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/apigwms/templates/configmap.yaml b/k8s/helm/apigwms/templates/configmap.yaml
index 7e93362d8..f3292ce72 100644
--- a/k8s/helm/apigwms/templates/configmap.yaml
+++ b/k8s/helm/apigwms/templates/configmap.yaml
@@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
+ internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
+ internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
+ internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
+ internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
+ internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc
diff --git a/k8s/helm/apigwms/templates/deployment.yaml b/k8s/helm/apigwms/templates/deployment.yaml
index 2c8a43050..8a4fd8942 100644
--- a/k8s/helm/apigwms/templates/deployment.yaml
+++ b/k8s/helm/apigwms/templates/deployment.yaml
@@ -36,6 +36,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:
diff --git a/k8s/helm/apigwms/values.yaml b/k8s/helm/apigwms/values.yaml
index f432a923a..58377ec5b 100644
--- a/k8s/helm/apigwms/values.yaml
+++ b/k8s/helm/apigwms/values.yaml
@@ -32,8 +32,33 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
+ - name: CatalogUrlHC
+ key: internalurls__catalog__hc
+ - name: BasketUrlHC
+ key: internalurls__basket__hc
+ - name: IdentityUrlHC
+ key: internalurls__identity__hc
+ - name: OrderingUrlHC
+ key: internalurls__ordering__hc
+ - name: MarketingUrlHC
+ key: internalurls__marketing__hc
+ - name: PaymentUrlHC
+ key: internalurls__payment__hc
+ - name: LocationUrlHC
+ key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
\ No newline at end of file
diff --git a/k8s/helm/apigwwm/templates/configmap.yaml b/k8s/helm/apigwwm/templates/configmap.yaml
index 2e27194d1..34c0e6979 100644
--- a/k8s/helm/apigwwm/templates/configmap.yaml
+++ b/k8s/helm/apigwwm/templates/configmap.yaml
@@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
+ internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
+ internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
+ internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
+ internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
+ internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc
diff --git a/k8s/helm/apigwwm/templates/deployment.yaml b/k8s/helm/apigwwm/templates/deployment.yaml
index 1e0e58888..5cbce9f22 100644
--- a/k8s/helm/apigwwm/templates/deployment.yaml
+++ b/k8s/helm/apigwwm/templates/deployment.yaml
@@ -36,6 +36,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:
diff --git a/k8s/helm/apigwwm/values.yaml b/k8s/helm/apigwwm/values.yaml
index a00e59deb..68cbb89c4 100644
--- a/k8s/helm/apigwwm/values.yaml
+++ b/k8s/helm/apigwwm/values.yaml
@@ -32,8 +32,33 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
+ - name: CatalogUrlHC
+ key: internalurls__catalog__hc
+ - name: BasketUrlHC
+ key: internalurls__basket__hc
+ - name: IdentityUrlHC
+ key: internalurls__identity__hc
+ - name: OrderingUrlHC
+ key: internalurls__ordering__hc
+ - name: MarketingUrlHC
+ key: internalurls__marketing__hc
+ - name: PaymentUrlHC
+ key: internalurls__payment__hc
+ - name: LocationUrlHC
+ key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
\ No newline at end of file
diff --git a/k8s/helm/apigwws/templates/configmap.yaml b/k8s/helm/apigwws/templates/configmap.yaml
index e5dc17201..dd5530f61 100644
--- a/k8s/helm/apigwws/templates/configmap.yaml
+++ b/k8s/helm/apigwws/templates/configmap.yaml
@@ -11,4 +11,11 @@ metadata:
heritage: {{ .Release.Service }}
data:
internalurls__identity: http://{{ .Values.app.svc.identity }}
+ internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
+ internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
+ internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
+ internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
+ internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc
diff --git a/k8s/helm/apigwws/templates/deployment.yaml b/k8s/helm/apigwws/templates/deployment.yaml
index b52e77042..4912d12bb 100644
--- a/k8s/helm/apigwws/templates/deployment.yaml
+++ b/k8s/helm/apigwws/templates/deployment.yaml
@@ -6,7 +6,6 @@ kind: Deployment
metadata:
name: {{ template "apigwws.fullname" . }}
labels:
- ufo: {{ $cfgname}}
app: {{ template "apigwws.name" . }}
chart: {{ template "apigwws.chart" . }}
release: {{ .Release.Name }}
@@ -36,6 +35,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:
diff --git a/k8s/helm/apigwws/values.yaml b/k8s/helm/apigwws/values.yaml
index 57ed34dd5..94b8a203f 100644
--- a/k8s/helm/apigwws/values.yaml
+++ b/k8s/helm/apigwws/values.yaml
@@ -32,8 +32,33 @@ env:
configmap:
- name: IdentityUrl
key: internalurls__identity
+ - name: CatalogUrlHC
+ key: internalurls__catalog__hc
+ - name: BasketUrlHC
+ key: internalurls__basket__hc
+ - name: IdentityUrlHC
+ key: internalurls__identity__hc
+ - name: OrderingUrlHC
+ key: internalurls__ordering__hc
+ - name: MarketingUrlHC
+ key: internalurls__marketing__hc
+ - name: PaymentUrlHC
+ key: internalurls__payment__hc
+ - name: LocationUrlHC
+ key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/app.yaml b/k8s/helm/app.yaml
index bd3988c5e..6b1c36922 100644
--- a/k8s/helm/app.yaml
+++ b/k8s/helm/app.yaml
@@ -22,12 +22,14 @@ app: # app global settings
payment: payment-api # ingress entry for payment api
locations: locations-api # ingress entry for locations api
marketing: marketing-api # ingress entry for marketing api
+ webhooks: webhooks-api # ingress entry for webhooks api
+ webhooksweb: webhooks-web # ingress entry for webhooks web demo client
svc:
basket: basket # service name for basket api
catalog: catalog # service name for catalog api
ordering: ordering # service name for ordering api
orderingbackgroundtasks: orderingbackgroundtasks # service name for orderingbackgroundtasks
- orderingsignalrhub: orderingsignalrhub # service name for orderingsignalrhub
+ orderingsignalrhub: ordering-signalrhub # service name for orderingsignalrhub
identity: identity # service name for identity api
mvc: webmvc # service name for web mvc
spa: webspa # service name for web spa
@@ -41,3 +43,5 @@ app: # app global settings
payment: payment # service name for payment api
locations: locations # service name for locations api
marketing: marketing # service name for marketing ap
+ webhooks: webhooks # service name for webhooks api
+ webhooksweb: webhooksweb # service name for webhooks web
diff --git a/k8s/helm/basket-api/templates/deployment.yaml b/k8s/helm/basket-api/templates/deployment.yaml
index ee283122c..d96c0cacf 100644
--- a/k8s/helm/basket-api/templates/deployment.yaml
+++ b/k8s/helm/basket-api/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/basket-api/values.yaml b/k8s/helm/basket-api/values.yaml
index 892fdf584..6c264afba 100644
--- a/k8s/helm/basket-api/values.yaml
+++ b/k8s/helm/basket-api/values.yaml
@@ -41,3 +41,15 @@ env:
values:
- name: OrchestratorType
value: 'K8S'
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/catalog-api/templates/deployment.yaml b/k8s/helm/catalog-api/templates/deployment.yaml
index 99e270af9..d7a424a99 100644
--- a/k8s/helm/catalog-api/templates/deployment.yaml
+++ b/k8s/helm/catalog-api/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/catalog-api/values.yaml b/k8s/helm/catalog-api/values.yaml
index e0306a824..836db6125 100644
--- a/k8s/helm/catalog-api/values.yaml
+++ b/k8s/helm/catalog-api/values.yaml
@@ -44,4 +44,16 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/deploy-all.ps1 b/k8s/helm/deploy-all.ps1
index 1239cc7af..1819c9a67 100644
--- a/k8s/helm/deploy-all.ps1
+++ b/k8s/helm/deploy-all.ps1
@@ -8,11 +8,19 @@ Param(
[parameter(Mandatory=$false)][bool]$clean=$true,
[parameter(Mandatory=$false)][string]$aksName="",
[parameter(Mandatory=$false)][string]$aksRg="",
- [parameter(Mandatory=$false)][string]$imageTag="latest"
-)
+ [parameter(Mandatory=$false)][string]$imageTag="latest",
+ [parameter(Mandatory=$false)][bool]$useLocalk8s=$false
+ )
$dns = $externalDns
+$ingressValuesFile="ingress_values.yaml"
+
+if ($useLocalk8s -eq $true) {
+ $ingressValuesFile="ingress_values_dockerk8s.yaml"
+ $dns="localhost"
+}
+
if ($externalDns -eq "aks") {
if ([string]::IsNullOrEmpty($aksName) -or [string]::IsNullOrEmpty($aksRg)) {
Write-Host "Error: When using -dns aks, MUST set -aksName and -aksRg too." -ForegroundColor Red
@@ -53,23 +61,23 @@ if (-not [string]::IsNullOrEmpty($registry)) {
Write-Host "Begin eShopOnContainers installation using Helm" -ForegroundColor Green
$infras = ("sql-data", "nosql-data", "rabbitmq", "keystore-data", "basket-data")
-$charts = ("eshop-common", "apigwmm", "apigwms", "apigwwm", "apigwws", "basket-api","catalog-api", "identity-api", "locations-api", "marketing-api", "mobileshoppingagg","ordering-api","ordering-backgroundtasks","ordering-signalrhub", "payment-api", "webmvc", "webshoppingagg", "webspa", "webstatus")
+$charts = ("eshop-common", "apigwmm", "apigwms", "apigwwm", "apigwws", "basket-api","catalog-api", "identity-api", "locations-api", "marketing-api", "mobileshoppingagg","ordering-api","ordering-backgroundtasks","ordering-signalrhub", "payment-api", "webmvc", "webshoppingagg", "webspa", "webstatus", "webhooks-api", "webhooks-web")
if ($deployInfrastructure) {
foreach ($infra in $infras) {
Write-Host "Installing infrastructure: $infra" -ForegroundColor Green
- helm install --values app.yaml --values inf.yaml --values ingress_values.yaml --set app.name=$appName --set inf.k8s.dns=$dns --name="$appName-$infra" $infra
+ helm install --values app.yaml --values inf.yaml --values $ingressValuesFile --set app.name=$appName --set inf.k8s.dns=$dns --name="$appName-$infra" $infra
}
}
foreach ($chart in $charts) {
Write-Host "Installing: $chart" -ForegroundColor Green
if ($useCustomRegistry) {
- helm install --set inf.registry.server=$registry --set inf.registry.login=$dockerUser --set inf.registry.pwd=$dockerPassword --set inf.registry.secretName=eshop-docker-scret --values app.yaml --values inf.yaml --values ingress_values.yaml --set app.name=$appName --set inf.k8s.dns=$dns --set image.tag=$imageTag --set image.pullPolicy=Always --name="$appName-$chart" $chart
+ helm install --set inf.registry.server=$registry --set inf.registry.login=$dockerUser --set inf.registry.pwd=$dockerPassword --set inf.registry.secretName=eshop-docker-scret --values app.yaml --values inf.yaml --values $ingressValuesFile --set app.name=$appName --set inf.k8s.dns=$dns --set image.tag=$imageTag --set image.pullPolicy=Always --name="$appName-$chart" $chart
}
else {
if ($chart -ne "eshop-common") { # eshop-common is ignored when no secret must be deployed
- helm install --values app.yaml --values inf.yaml --values ingress_values.yaml --set app.name=$appName --set inf.k8s.dns=$dns --set image.tag=$imageTag --set image.pullPolicy=Always --name="$appName-$chart" $chart
+ helm install --values app.yaml --values inf.yaml --values $ingressValuesFile --set app.name=$appName --set inf.k8s.dns=$dns --set image.tag=$imageTag --set image.pullPolicy=Always --name="$appName-$chart" $chart
}
}
}
diff --git a/k8s/helm/identity-api/templates/configmap.yaml b/k8s/helm/identity-api/templates/configmap.yaml
index 3a71f1a81..24d71b699 100644
--- a/k8s/helm/identity-api/templates/configmap.yaml
+++ b/k8s/helm/identity-api/templates/configmap.yaml
@@ -9,6 +9,8 @@
{{- $mobileshoppingagg := include "url-of" (list .Values.app.ingress.entries.mobileshoppingagg .) -}}
{{- $webhoppingagg := include "url-of" (list .Values.app.ingress.entries.webshoppingagg .) -}}
{{- $xamarincallback := include "url-of" (list "xamarincallback" .) -}}
+{{- $webhooks_url := include "url-of" (list .Values.app.ingress.entries.webhooks .) -}}
+{{- $webhooksweb_url := include "url-of" (list .Values.app.ingress.entries.webhooksweb .) -}}
apiVersion: v1
kind: ConfigMap
@@ -32,4 +34,7 @@ data:
mobileshoppingagg_e: http://{{ $mobileshoppingagg }}
webshoppingagg_e: http://{{ $webhoppingagg }}
xamarin_callback_e: http://{{ $xamarincallback }}
+ webhooksapi_e: http://{{ $webhooks_url }}
+ webhooksweb_e: http://{{ $webhooksweb_url }}
+
diff --git a/k8s/helm/identity-api/templates/deployment.yaml b/k8s/helm/identity-api/templates/deployment.yaml
index 753725a9f..0a4ee2722 100644
--- a/k8s/helm/identity-api/templates/deployment.yaml
+++ b/k8s/helm/identity-api/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/identity-api/values.yaml b/k8s/helm/identity-api/values.yaml
index 9777db355..c0fd38192 100644
--- a/k8s/helm/identity-api/values.yaml
+++ b/k8s/helm/identity-api/values.yaml
@@ -54,6 +54,10 @@ env:
key: webshoppingagg_e
- name: XamarinCallback
key: xamarin_callback_e
+ - name: WebhooksApiClient
+ key: webhooksapi_e
+ - name: WebhooksWebClient
+ key: webhooksweb_e
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
@@ -61,4 +65,15 @@ env:
value: 'K8S'
- name: IsClusterEnv
value: 'True'
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
\ No newline at end of file
diff --git a/k8s/helm/inf.yaml b/k8s/helm/inf.yaml
index 4c0180f45..b6ff102b0 100644
--- a/k8s/helm/inf.yaml
+++ b/k8s/helm/inf.yaml
@@ -14,6 +14,10 @@ inf:
db: OrderingDb # Ordering API SQL db name
identity:
db: IdentityDb # Ordering API SQL db name
+ marketing:
+ db: MarketingDb # Marketing API SQL db name
+ webhooks:
+ db: WebhooksDb # Webhooks DB
mongo:
# host: my-nosql-data # Uncomment to use specify custom mongo host. By default nosql-data is used
locations:
diff --git a/k8s/helm/ingress_values_dockerk8s.yaml b/k8s/helm/ingress_values_dockerk8s.yaml
new file mode 100644
index 000000000..75597aac9
--- /dev/null
+++ b/k8s/helm/ingress_values_dockerk8s.yaml
@@ -0,0 +1,5 @@
+ingress:
+ annotations:
+ kubernetes.io/ingress.class: "nginx"
+ ingress.kubernetes.io/ssl-redirect: "false"
+ nginx.ingress.kubernetes.io/ssl-redirect: "false"
diff --git a/k8s/helm/locations-api/templates/deployment.yaml b/k8s/helm/locations-api/templates/deployment.yaml
index c8f3f00cd..9667eb967 100644
--- a/k8s/helm/locations-api/templates/deployment.yaml
+++ b/k8s/helm/locations-api/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/locations-api/values.yaml b/k8s/helm/locations-api/values.yaml
index 99c130def..4718f2a0b 100644
--- a/k8s/helm/locations-api/values.yaml
+++ b/k8s/helm/locations-api/values.yaml
@@ -52,4 +52,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/marketing-api/templates/configmap.yaml b/k8s/helm/marketing-api/templates/configmap.yaml
index ea99a1b5a..45f21e57d 100644
--- a/k8s/helm/marketing-api/templates/configmap.yaml
+++ b/k8s/helm/marketing-api/templates/configmap.yaml
@@ -2,6 +2,7 @@
{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}}
{{- $webshoppingapigw := include "url-of" (list .Values.app.ingress.entries.webshoppingapigw .) -}}
{{- $mongo := include "mongo-name" . -}}
+{{- $sqlsrv := include "sql-name" . -}}
apiVersion: v1
kind: ConfigMap
@@ -19,6 +20,7 @@ data:
all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}"
internalurls__IdentityUrl: http://{{ .Values.app.svc.identity }}
urls__IdentityUrl: {{ $identity }}
- marketing__ConnectionString: mongodb://{{ $mongo }}
- marketing__Database: {{ .Values.inf.mongo.marketing.database }}
+ marketing__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.marketing.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
+ marketing__MongoConnectionString: mongodb://{{ $mongo }}
+ marketing__MongoDatabase: {{ .Values.inf.mongo.marketing.database }}
marketing__PicBaseUrl: http://{{ $webshoppingapigw }}/api/v1/c/catalog/items/[0]/pic/
\ No newline at end of file
diff --git a/k8s/helm/marketing-api/templates/deployment.yaml b/k8s/helm/marketing-api/templates/deployment.yaml
index e0cbeb3ce..c49026c1b 100644
--- a/k8s/helm/marketing-api/templates/deployment.yaml
+++ b/k8s/helm/marketing-api/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/marketing-api/values.yaml b/k8s/helm/marketing-api/values.yaml
index 8779685d6..00bebf27f 100644
--- a/k8s/helm/marketing-api/values.yaml
+++ b/k8s/helm/marketing-api/values.yaml
@@ -44,8 +44,10 @@ env:
key: urls__IdentityUrl
- name: ConnectionString
key: marketing__ConnectionString
- - name: Database
- key: marketing__Database
+ - name: MongoConnectionString
+ key: marketing__MongoConnectionString
+ - name: MongoDatabase
+ key: marketing__MongoDatabase
- name: PicBaseUrl
key: marketing__PicBaseUrl
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
@@ -54,4 +56,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/mobileshoppingagg/templates/configmap.yaml b/k8s/helm/mobileshoppingagg/templates/configmap.yaml
index 10eb543b2..d3e935409 100644
--- a/k8s/helm/mobileshoppingagg/templates/configmap.yaml
+++ b/k8s/helm/mobileshoppingagg/templates/configmap.yaml
@@ -17,3 +17,10 @@ data:
internalurls__catalog: http://{{ .Values.app.svc.catalog }}
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__ordering: http://{{ .Values.app.svc.ordering }}
+ internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
+ internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
+ internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
+ internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
+ internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc
diff --git a/k8s/helm/mobileshoppingagg/templates/deployment.yaml b/k8s/helm/mobileshoppingagg/templates/deployment.yaml
index 5e81da7bf..0f23b3be6 100644
--- a/k8s/helm/mobileshoppingagg/templates/deployment.yaml
+++ b/k8s/helm/mobileshoppingagg/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/mobileshoppingagg/values.yaml b/k8s/helm/mobileshoppingagg/values.yaml
index 039695024..fd26c7794 100644
--- a/k8s/helm/mobileshoppingagg/values.yaml
+++ b/k8s/helm/mobileshoppingagg/values.yaml
@@ -40,6 +40,20 @@ env:
key: internalurls__ordering
- name: urls__identity
key: internalurls__identity
+ - name: CatalogUrlHC
+ key: internalurls__catalog__hc
+ - name: BasketUrlHC
+ key: internalurls__basket__hc
+ - name: IdentityUrlHC
+ key: internalurls__identity__hc
+ - name: OrderingUrlHC
+ key: internalurls__ordering__hc
+ - name: MarketingUrlHC
+ key: internalurls__marketing__hc
+ - name: PaymentUrlHC
+ key: internalurls__payment__hc
+ - name: LocationUrlHC
+ key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
@@ -50,4 +64,15 @@ env:
value: 'K8S'
- name: IsClusterEnv
value: 'True'
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/ordering-api/templates/deployment.yaml b/k8s/helm/ordering-api/templates/deployment.yaml
index 19845c251..a99ccdc28 100644
--- a/k8s/helm/ordering-api/templates/deployment.yaml
+++ b/k8s/helm/ordering-api/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/ordering-api/values.yaml b/k8s/helm/ordering-api/values.yaml
index c4a7cdf99..c717d2793 100644
--- a/k8s/helm/ordering-api/values.yaml
+++ b/k8s/helm/ordering-api/values.yaml
@@ -50,4 +50,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/ordering-backgroundtasks/templates/deployment.yaml b/k8s/helm/ordering-backgroundtasks/templates/deployment.yaml
index cb57ca18d..017f9f3dd 100644
--- a/k8s/helm/ordering-backgroundtasks/templates/deployment.yaml
+++ b/k8s/helm/ordering-backgroundtasks/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/ordering-backgroundtasks/values.yaml b/k8s/helm/ordering-backgroundtasks/values.yaml
index adfa20c03..d065f0345 100644
--- a/k8s/helm/ordering-backgroundtasks/values.yaml
+++ b/k8s/helm/ordering-backgroundtasks/values.yaml
@@ -56,4 +56,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/ordering-signalrhub/templates/configmap.yaml b/k8s/helm/ordering-signalrhub/templates/configmap.yaml
index cec9565f0..addcf7e9d 100644
--- a/k8s/helm/ordering-signalrhub/templates/configmap.yaml
+++ b/k8s/helm/ordering-signalrhub/templates/configmap.yaml
@@ -15,4 +15,4 @@ data:
all__InstrumentationKey: {{ .Values.inf.appinsights.key }}
all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}"
signalr__StoreConnectionString: {{ .Values.inf.redis.keystore.constr }}
- urls__IdentityUrl: {{ $identity }}
\ No newline at end of file
+ urls__IdentityUrl: http://{{ $identity }}
\ No newline at end of file
diff --git a/k8s/helm/payment-api/templates/deployment.yaml b/k8s/helm/payment-api/templates/deployment.yaml
index 8b414145f..8b01f7394 100644
--- a/k8s/helm/payment-api/templates/deployment.yaml
+++ b/k8s/helm/payment-api/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/payment-api/values.yaml b/k8s/helm/payment-api/values.yaml
index 0ee6dd4d9..341e4e1a9 100644
--- a/k8s/helm/payment-api/values.yaml
+++ b/k8s/helm/payment-api/values.yaml
@@ -42,4 +42,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
-
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/webhooks-api/.helmignore b/k8s/helm/webhooks-api/.helmignore
new file mode 100644
index 000000000..f0c131944
--- /dev/null
+++ b/k8s/helm/webhooks-api/.helmignore
@@ -0,0 +1,21 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/k8s/helm/webhooks-api/Chart.yaml b/k8s/helm/webhooks-api/Chart.yaml
new file mode 100644
index 000000000..f8e950782
--- /dev/null
+++ b/k8s/helm/webhooks-api/Chart.yaml
@@ -0,0 +1,5 @@
+apiVersion: v1
+appVersion: "1.0"
+description: A Helm chart for Kubernetes
+name: webhooks-api
+version: 0.1.0
diff --git a/k8s/helm/webhooks-api/templates/NOTES.txt b/k8s/helm/webhooks-api/templates/NOTES.txt
new file mode 100644
index 000000000..818b99d1b
--- /dev/null
+++ b/k8s/helm/webhooks-api/templates/NOTES.txt
@@ -0,0 +1,8 @@
+eShop Ordering API installed.
+-----------------------------
+
+This API is not directly exposed outside cluster. If need to access it use:
+
+export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "webhooks-api.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+echo "Visit http://127.0.0.1:8080 to use your application"
+kubectl port-forward $POD_NAME 8080:80
diff --git a/k8s/helm/webhooks-api/templates/_helpers.tpl b/k8s/helm/webhooks-api/templates/_helpers.tpl
new file mode 100644
index 000000000..3742516b7
--- /dev/null
+++ b/k8s/helm/webhooks-api/templates/_helpers.tpl
@@ -0,0 +1,32 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "webhooks-api.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "webhooks-api.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "webhooks-api.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
diff --git a/k8s/helm/webhooks-api/templates/_names.tpl b/k8s/helm/webhooks-api/templates/_names.tpl
new file mode 100644
index 000000000..39ee485ef
--- /dev/null
+++ b/k8s/helm/webhooks-api/templates/_names.tpl
@@ -0,0 +1,51 @@
+{{- define "suffix-name" -}}
+{{- if .Values.app.name -}}
+{{- .Values.app.name -}}
+{{- else -}}
+{{- .Release.Name -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "sql-name" -}}
+{{- if .Values.inf.sql.host -}}
+{{- .Values.inf.sql.host -}}
+{{- else -}}
+{{- printf "%s" "sql-data" -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "mongo-name" -}}
+{{- if .Values.inf.mongo.host -}}
+{{- .Values.inf.mongo.host -}}
+{{- else -}}
+{{- printf "%s" "nosql-data" -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "url-of" -}}
+{{- $name := first .}}
+{{- $ctx := last .}}
+{{- if eq $name "" -}}
+{{- $ctx.Values.inf.k8s.dns -}}
+{{- else -}}
+{{- printf "%s/%s" $ctx.Values.inf.k8s.dns $name -}} {{/*Value is just / */}}
+{{- end -}}
+{{- end -}}
+
+
+{{- define "pathBase" -}}
+{{- if .Values.inf.k8s.suffix -}}
+{{- $suffix := include "suffix-name" . -}}
+{{- printf "%s-%s" .Values.pathBase $suffix -}}
+{{- else -}}
+{{- .Values.pathBase -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "fqdn-image" -}}
+{{- if .Values.inf.registry -}}
+{{- printf "%s/%s" .Values.inf.registry.server .Values.image.repository -}}
+{{- else -}}
+{{- .Values.image.repository -}}
+{{- end -}}
+{{- end -}}
\ No newline at end of file
diff --git a/k8s/helm/webhooks-api/templates/configmap.yaml b/k8s/helm/webhooks-api/templates/configmap.yaml
new file mode 100644
index 000000000..a18126858
--- /dev/null
+++ b/k8s/helm/webhooks-api/templates/configmap.yaml
@@ -0,0 +1,20 @@
+{{- $name := include "webhooks-api.fullname" . -}}
+{{- $sqlsrv := include "sql-name" . -}}
+{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}}
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: "cfg-{{ $name }}"
+ labels:
+ app: {{ template "webhooks-api.name" . }}
+ chart: {{ template "webhooks-api.chart" .}}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+data:
+ webhooks__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.webhooks.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
+ urls__IdentityUrl: http://{{ $identity }}
+ urls__IdentityUrlExternal: http://{{ $identity }}
+ all__EventBusConnection: {{ .Values.inf.eventbus.constr }}
+ all__InstrumentationKey: {{ .Values.inf.appinsights.key }}
+ all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}"
\ No newline at end of file
diff --git a/k8s/helm/webhooks-api/templates/deployment.yaml b/k8s/helm/webhooks-api/templates/deployment.yaml
new file mode 100644
index 000000000..9eef1d6f1
--- /dev/null
+++ b/k8s/helm/webhooks-api/templates/deployment.yaml
@@ -0,0 +1,71 @@
+{{- $name := include "webhooks-api.fullname" . -}}
+{{- $cfgname := printf "%s-%s" "cfg" $name -}}
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: {{ template "webhooks-api.fullname" . }}
+ labels:
+ ufo: {{ $cfgname}}
+ app: {{ template "webhooks-api.name" . }}
+ chart: {{ template "webhooks-api.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: {{ .Values.replicaCount }}
+ selector:
+ matchLabels:
+ app: {{ template "webhooks-api.name" . }}
+ release: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "webhooks-api.name" . }}
+ release: {{ .Release.Name }}
+ spec:
+ {{ if .Values.inf.registry -}}
+ imagePullSecrets:
+ - name: {{ .Values.inf.registry.secretName }}
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ env:
+ - name: PATH_BASE
+ value: {{ include "pathBase" . }}
+ - name: k8sname
+ value: {{ .Values.clusterName }}
+ {{- if .Values.env.values -}}
+ {{- range .Values.env.values }}
+ - name: {{ .name }}
+ value: {{ .value | quote }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.env.configmap -}}
+ {{- range .Values.env.configmap }}
+ - name: {{ .name }}
+ valueFrom:
+ configMapKeyRef:
+ name: {{ $cfgname }}
+ key: {{ .key }}
+ {{- end -}}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: 80
+ protocol: TCP
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+
diff --git a/k8s/helm/webhooks-api/templates/ingress.yaml b/k8s/helm/webhooks-api/templates/ingress.yaml
new file mode 100644
index 000000000..293f8e47e
--- /dev/null
+++ b/k8s/helm/webhooks-api/templates/ingress.yaml
@@ -0,0 +1,33 @@
+{{- if .Values.ingress.enabled -}}
+{{- $ingressPath := include "pathBase" . -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: {{ template "webhooks-api.fullname" . }}
+ labels:
+ app: {{ template "webhooks-api.name" . }}
+ chart: {{ template "webhooks-api.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- with .Values.ingress.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+spec:
+{{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ - {{ .Values.inf.k8s.dns }}
+ secretName: {{ .secretName }}
+ {{- end }}
+{{- end }}
+ rules:
+ - host: {{ .Values.inf.k8s.dns }}
+ http:
+ paths:
+ - path: {{ $ingressPath }}
+ backend:
+ serviceName: {{ .Values.app.svc.webhooks }}
+ servicePort: http
+{{- end }}
diff --git a/k8s/helm/webhooks-api/templates/service.yaml b/k8s/helm/webhooks-api/templates/service.yaml
new file mode 100644
index 000000000..d8a02ba65
--- /dev/null
+++ b/k8s/helm/webhooks-api/templates/service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ .Values.app.svc.webhooks }}
+ labels:
+ app: {{ template "webhooks-api.name" . }}
+ chart: {{ template "webhooks-api.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app: {{ template "webhooks-api.name" . }}
+ release: {{ .Release.Name }}
diff --git a/k8s/helm/webhooks-api/values.yaml b/k8s/helm/webhooks-api/values.yaml
new file mode 100644
index 000000000..f6b1957e9
--- /dev/null
+++ b/k8s/helm/webhooks-api/values.yaml
@@ -0,0 +1,53 @@
+replicaCount: 1
+clusterName: eshop-aks
+pathBase: /webhooks-api
+
+image:
+ repository: eshop/webhooks.api
+ tag: latest
+ pullPolicy: IfNotPresent
+
+service:
+ type: ClusterIP
+ port: 80
+
+ingress:
+ enabled: true
+ annotations: {}
+ hosts:
+ - chart-example.local
+ tls: []
+
+resources: {}
+
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+# env defines the environment variables that will be declared in the pod
+env:
+ urls:
+ # configmap declares variables which value is taken from the config map defined in template configmap.yaml (name is name of var and key the key in configmap).
+ configmap:
+ - name: ConnectionString
+ key: webhooks__ConnectionString
+ - name: ApplicationInsights__InstrumentationKey
+ key: all__InstrumentationKey
+ - name: EventBusConnection
+ key: all__EventBusConnection
+ - name: AzureServiceBusEnabled
+ key: all__UseAzureServiceBus
+ - name: IdentityUrl
+ key: urls__IdentityUrl
+ - name: IdentityUrlExternal
+ key: urls__IdentityUrlExternal
+ # values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
+ values:
+ - name: ASPNETCORE_ENVIRONMENT
+ value: Development
+ - name: OrchestratorType
+ value: 'K8S'
+
diff --git a/k8s/helm/webhooks-web/.helmignore b/k8s/helm/webhooks-web/.helmignore
new file mode 100644
index 000000000..f0c131944
--- /dev/null
+++ b/k8s/helm/webhooks-web/.helmignore
@@ -0,0 +1,21 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/k8s/helm/webhooks-web/Chart.yaml b/k8s/helm/webhooks-web/Chart.yaml
new file mode 100644
index 000000000..420b4f16d
--- /dev/null
+++ b/k8s/helm/webhooks-web/Chart.yaml
@@ -0,0 +1,5 @@
+apiVersion: v1
+appVersion: "1.0"
+description: A Helm chart for Kubernetes
+name: webhooks-web
+version: 0.1.0
diff --git a/k8s/helm/webhooks-web/templates/NOTES.txt b/k8s/helm/webhooks-web/templates/NOTES.txt
new file mode 100644
index 000000000..b7f7f97ba
--- /dev/null
+++ b/k8s/helm/webhooks-web/templates/NOTES.txt
@@ -0,0 +1,8 @@
+eShop Ordering API installed.
+-----------------------------
+
+This API is not directly exposed outside cluster. If need to access it use:
+
+export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "webhooks-web.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+echo "Visit http://127.0.0.1:8080 to use your application"
+kubectl port-forward $POD_NAME 8080:80
diff --git a/k8s/helm/webhooks-web/templates/_helpers.tpl b/k8s/helm/webhooks-web/templates/_helpers.tpl
new file mode 100644
index 000000000..cbc856713
--- /dev/null
+++ b/k8s/helm/webhooks-web/templates/_helpers.tpl
@@ -0,0 +1,32 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "webhooks-web.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "webhooks-web.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "webhooks-web.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
diff --git a/k8s/helm/webhooks-web/templates/_names.tpl b/k8s/helm/webhooks-web/templates/_names.tpl
new file mode 100644
index 000000000..39ee485ef
--- /dev/null
+++ b/k8s/helm/webhooks-web/templates/_names.tpl
@@ -0,0 +1,51 @@
+{{- define "suffix-name" -}}
+{{- if .Values.app.name -}}
+{{- .Values.app.name -}}
+{{- else -}}
+{{- .Release.Name -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "sql-name" -}}
+{{- if .Values.inf.sql.host -}}
+{{- .Values.inf.sql.host -}}
+{{- else -}}
+{{- printf "%s" "sql-data" -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "mongo-name" -}}
+{{- if .Values.inf.mongo.host -}}
+{{- .Values.inf.mongo.host -}}
+{{- else -}}
+{{- printf "%s" "nosql-data" -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "url-of" -}}
+{{- $name := first .}}
+{{- $ctx := last .}}
+{{- if eq $name "" -}}
+{{- $ctx.Values.inf.k8s.dns -}}
+{{- else -}}
+{{- printf "%s/%s" $ctx.Values.inf.k8s.dns $name -}} {{/*Value is just / */}}
+{{- end -}}
+{{- end -}}
+
+
+{{- define "pathBase" -}}
+{{- if .Values.inf.k8s.suffix -}}
+{{- $suffix := include "suffix-name" . -}}
+{{- printf "%s-%s" .Values.pathBase $suffix -}}
+{{- else -}}
+{{- .Values.pathBase -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "fqdn-image" -}}
+{{- if .Values.inf.registry -}}
+{{- printf "%s/%s" .Values.inf.registry.server .Values.image.repository -}}
+{{- else -}}
+{{- .Values.image.repository -}}
+{{- end -}}
+{{- end -}}
\ No newline at end of file
diff --git a/k8s/helm/webhooks-web/templates/configmap.yaml b/k8s/helm/webhooks-web/templates/configmap.yaml
new file mode 100644
index 000000000..8852c7586
--- /dev/null
+++ b/k8s/helm/webhooks-web/templates/configmap.yaml
@@ -0,0 +1,19 @@
+{{- $name := include "webhooks-web.fullname" . -}}
+{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}}
+{{- $webhooksweb := include "url-of" (list .Values.app.ingress.entries.webhooksweb .) -}}
+{{- $webhooks := include "url-of" (list .Values.app.ingress.entries.webhooks .) -}}
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: "cfg-{{ $name }}"
+ labels:
+ app: {{ template "webhooks-web.name" . }}
+ chart: {{ template "webhooks-web.chart" .}}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+data:
+ urls__webhooks: http://{{ $webhooks }}
+ identity_e: http://{{ $identity }}
+ webhooksweb_e: http://{{ $webhooksweb }}
+ urls_webhooksweb: http://{{ .Values.app.svc.webhooksweb }}
diff --git a/k8s/helm/webhooks-web/templates/deployment.yaml b/k8s/helm/webhooks-web/templates/deployment.yaml
new file mode 100644
index 000000000..4c930124a
--- /dev/null
+++ b/k8s/helm/webhooks-web/templates/deployment.yaml
@@ -0,0 +1,71 @@
+{{- $name := include "webhooks-web.fullname" . -}}
+{{- $cfgname := printf "%s-%s" "cfg" $name -}}
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: {{ template "webhooks-web.fullname" . }}
+ labels:
+ ufo: {{ $cfgname}}
+ app: {{ template "webhooks-web.name" . }}
+ chart: {{ template "webhooks-web.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: {{ .Values.replicaCount }}
+ selector:
+ matchLabels:
+ app: {{ template "webhooks-web.name" . }}
+ release: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "webhooks-web.name" . }}
+ release: {{ .Release.Name }}
+ spec:
+ {{ if .Values.inf.registry -}}
+ imagePullSecrets:
+ - name: {{ .Values.inf.registry.secretName }}
+ {{- end }}
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ env:
+ - name: PATH_BASE
+ value: {{ include "pathBase" . }}
+ - name: k8sname
+ value: {{ .Values.clusterName }}
+ {{- if .Values.env.values -}}
+ {{- range .Values.env.values }}
+ - name: {{ .name }}
+ value: {{ .value | quote }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.env.configmap -}}
+ {{- range .Values.env.configmap }}
+ - name: {{ .name }}
+ valueFrom:
+ configMapKeyRef:
+ name: {{ $cfgname }}
+ key: {{ .key }}
+ {{- end -}}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: 80
+ protocol: TCP
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+
diff --git a/k8s/helm/webhooks-web/templates/ingress.yaml b/k8s/helm/webhooks-web/templates/ingress.yaml
new file mode 100644
index 000000000..e725999f0
--- /dev/null
+++ b/k8s/helm/webhooks-web/templates/ingress.yaml
@@ -0,0 +1,33 @@
+{{- if .Values.ingress.enabled -}}
+{{- $ingressPath := include "pathBase" . -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: {{ template "webhooks-web.fullname" . }}
+ labels:
+ app: {{ template "webhooks-web.name" . }}
+ chart: {{ template "webhooks-web.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- with .Values.ingress.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+spec:
+{{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ - {{ .Values.inf.k8s.dns }}
+ secretName: {{ .secretName }}
+ {{- end }}
+{{- end }}
+ rules:
+ - host: {{ .Values.inf.k8s.dns }}
+ http:
+ paths:
+ - path: {{ $ingressPath }}
+ backend:
+ serviceName: {{ .Values.app.svc.webhooksweb }}
+ servicePort: http
+{{- end }}
diff --git a/k8s/helm/webhooks-web/templates/service.yaml b/k8s/helm/webhooks-web/templates/service.yaml
new file mode 100644
index 000000000..873ebcc0e
--- /dev/null
+++ b/k8s/helm/webhooks-web/templates/service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ .Values.app.svc.webhooksweb }}
+ labels:
+ app: {{ template "webhooks-web.name" . }}
+ chart: {{ template "webhooks-web.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app: {{ template "webhooks-web.name" . }}
+ release: {{ .Release.Name }}
diff --git a/k8s/helm/webhooks-web/values.yaml b/k8s/helm/webhooks-web/values.yaml
new file mode 100644
index 000000000..0e5b04b57
--- /dev/null
+++ b/k8s/helm/webhooks-web/values.yaml
@@ -0,0 +1,52 @@
+replicaCount: 1
+clusterName: eshop-aks
+pathBase: /webhooks-web
+
+image:
+ repository: eshop/webhooks.client
+ tag: latest
+ pullPolicy: IfNotPresent
+
+service:
+ type: ClusterIP
+ port: 80
+
+ingress:
+ enabled: true
+ annotations: {}
+ hosts:
+ - chart-example.local
+ tls: []
+
+resources: {}
+
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+# env defines the environment variables that will be declared in the pod
+env:
+ urls:
+ # configmap declares variables which value is taken from the config map defined in template configmap.yaml (name is name of var and key the key in configmap).
+ configmap:
+ - name: WebhooksUrl
+ key: urls__webhooks
+ - name: IdentityUrl
+ key: identity_e
+ - name: CallbackUrl
+ key: webhooksweb_e
+ - name: SelfUrl
+ key: webhooksweb_e
+ # values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
+ values:
+ - name: ASPNETCORE_ENVIRONMENT
+ value: Production
+ - name: OrchestratorType
+ value: 'K8S'
+ - name: Token
+ value: "WebHooks-Demo-Web" # Can use whatever you want
+
+
diff --git a/k8s/helm/webmvc/templates/configmap.yaml b/k8s/helm/webmvc/templates/configmap.yaml
index 9d120fe7b..60dacdadd 100644
--- a/k8s/helm/webmvc/templates/configmap.yaml
+++ b/k8s/helm/webmvc/templates/configmap.yaml
@@ -21,12 +21,9 @@ data:
webmvc__keystore: {{ .Values.inf.redis.keystore.constr }}
internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }}
internalurls__apigwwm: http://{{ .Values.app.svc.webmarketingapigw }}
- internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
- internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
- internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
- internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
- internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__apigwwm__hc: http://{{ .Values.app.svc.webmarketingapigw }}/hc
urls__apigwws: http://{{ $webshoppingapigw }}
urls__mvc: http://{{ $mvc }}
urls__IdentityUrl: http://{{ $identity }}
diff --git a/k8s/helm/webmvc/values.yaml b/k8s/helm/webmvc/values.yaml
index f37dc104e..f4d077fc2 100644
--- a/k8s/helm/webmvc/values.yaml
+++ b/k8s/helm/webmvc/values.yaml
@@ -46,18 +46,12 @@ env:
key: urls__IdentityUrl
- name: MarketingUrl
key: internalurls__apigwwm
- - name: BasketUrlHC
- key: internalurls__basket__hc
- - name: CatalogUrlHC
- key: internalurls__catalog__hc
+ - name: PurchaseUrlHC
+ key: internalurls__apigwws__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- - name: OrderingUrlHC
- key: internalurls__ordering__hc
- name: MarketingUrlHC
- key: internalurls__marketing__hc
- - name: PaymentUrlHC
- key: internalurls__payment__hc
+ key: internalurls__apigwwm__hc
- name: SignalrHubUrl
key: urls__apigwws
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
diff --git a/k8s/helm/webshoppingagg/templates/configmap.yaml b/k8s/helm/webshoppingagg/templates/configmap.yaml
index c03f2272d..b4d3da041 100644
--- a/k8s/helm/webshoppingagg/templates/configmap.yaml
+++ b/k8s/helm/webshoppingagg/templates/configmap.yaml
@@ -17,3 +17,10 @@ data:
internalurls__catalog: http://{{ .Values.app.svc.catalog }}
internalurls__identity: http://{{ .Values.app.svc.identity }}
internalurls__ordering: http://{{ .Values.app.svc.ordering }}
+ internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
+ internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
+ internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
+ internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
+ internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__location__hc: http://{{ .Values.app.svc.locations }}/hc
diff --git a/k8s/helm/webshoppingagg/templates/deployment.yaml b/k8s/helm/webshoppingagg/templates/deployment.yaml
index 9d557b0e0..8007c74c8 100644
--- a/k8s/helm/webshoppingagg/templates/deployment.yaml
+++ b/k8s/helm/webshoppingagg/templates/deployment.yaml
@@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
+ {{ if .Values.probes -}}
+ {{- if .Values.probes.liveness -}}
+ livenessProbe:
+ httpGet:
+ port: {{ .Values.probes.liveness.port }}
+ path: {{ .Values.probes.liveness.path }}
+ initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
+ {{- end -}}
+ {{- end -}}
+ {{- if .Values.probes -}}
+ {{- if .Values.probes.readiness }}
+ readinessProbe:
+ httpGet:
+ port: {{ .Values.probes.readiness.port }}
+ path: {{ .Values.probes.readiness.path }}
+ initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
+ periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
+ timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
+ {{- end -}}
+ {{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
diff --git a/k8s/helm/webshoppingagg/values.yaml b/k8s/helm/webshoppingagg/values.yaml
index 9f855d796..f4f2c5fd7 100644
--- a/k8s/helm/webshoppingagg/values.yaml
+++ b/k8s/helm/webshoppingagg/values.yaml
@@ -40,6 +40,20 @@ env:
key: internalurls__ordering
- name: urls__identity
key: internalurls__identity
+ - name: CatalogUrlHC
+ key: internalurls__catalog__hc
+ - name: BasketUrlHC
+ key: internalurls__basket__hc
+ - name: IdentityUrlHC
+ key: internalurls__identity__hc
+ - name: OrderingUrlHC
+ key: internalurls__ordering__hc
+ - name: MarketingUrlHC
+ key: internalurls__marketing__hc
+ - name: PaymentUrlHC
+ key: internalurls__payment__hc
+ - name: LocationUrlHC
+ key: internalurls__location__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
@@ -50,4 +64,16 @@ env:
value: 'K8S'
- name: IsClusterEnv
value: 'True'
+probes:
+ liveness:
+ path: /liveness
+ initialDelaySeconds: 10
+ periodSeconds: 15
+ port: 80
+ readiness:
+ path: /hc
+ timeoutSeconds: 5
+ initialDelaySeconds: 90
+ periodSeconds: 60
+ port: 80
diff --git a/k8s/helm/webspa/templates/configmap.yaml b/k8s/helm/webspa/templates/configmap.yaml
index bf03ff8cd..7d4651a31 100644
--- a/k8s/helm/webspa/templates/configmap.yaml
+++ b/k8s/helm/webspa/templates/configmap.yaml
@@ -20,12 +20,9 @@ data:
all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}"
webspa__keystore: {{ .Values.inf.redis.keystore.constr }}
internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }}
- internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
- internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc
internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
- internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
- internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
- internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ internalurls__apigwwm__hc: http://{{ .Values.app.svc.webmarketingapigw }}/hc
urls__apigwws: http://{{ $webshoppingapigw }}
urls__spa: http://{{ $spa }}
urls__IdentityUrl: http://{{ $identity }}
diff --git a/k8s/helm/webspa/values.yaml b/k8s/helm/webspa/values.yaml
index e07909f6e..056b58a0d 100644
--- a/k8s/helm/webspa/values.yaml
+++ b/k8s/helm/webspa/values.yaml
@@ -44,18 +44,12 @@ env:
key: urls__IdentityUrl
- name: MarketingUrl
key: urls__apigwwm
- - name: BasketUrlHC
- key: internalurls__basket__hc
- - name: CatalogUrlHC
- key: internalurls__catalog__hc
+ - name: PurchaseUrlHC
+ key: internalurls__apigwws__hc
- name: IdentityUrlHC
key: internalurls__identity__hc
- - name: OrderingUrlHC
- key: internalurls__ordering__hc
- name: MarketingUrlHC
- key: internalurls__marketing__hc
- - name: PaymentUrlHC
- key: internalurls__payment__hc
+ key: internalurls__apigwwm__hc
- name: SignalrHubUrl
key: urls__apigwws
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
diff --git a/k8s/helm/webstatus/templates/configmap.yaml b/k8s/helm/webstatus/templates/configmap.yaml
index 805e33165..3f32d7adc 100644
--- a/k8s/helm/webstatus/templates/configmap.yaml
+++ b/k8s/helm/webstatus/templates/configmap.yaml
@@ -19,18 +19,37 @@ data:
all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}"
all_EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}"
webstatus__keystore: {{ .Values.inf.redis.keystore.constr }}
- internalurls__apigwws: http://{{ .Values.app.svc.webshoppingapigw }}
- internalurls__apigwwm: http://{{ .Values.app.svc.webmarketingapigw }}
- internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
- internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
- internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
- internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
- internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
- internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
- internalurls__locations__hc: http://{{ .Values.app.svc.locations }}/hc
- internalurls__orderingbackground__hc: http://{{ .Values.app.svc.orderingbackgroundtasks }}/hc
+ name__mvc__hc: WebMVC HTTP Check
internalurls__mvc__hc: http://{{ .Values.app.svc.mvc }}/hc
+ name__spa__hc: WebSPA HTTP Check
internalurls__spa__hc: http://{{ .Values.app.svc.spa }}/hc
- urls__apigwws: http://{{ $webshoppingapigw }}
- urls__mvc: http://{{ $mvc }}
- urls__IdentityUrl: http://{{ $identity }}
+ name__apigwws__hc: Web Shopping API GW HTTP Check
+ internalurls__apigwws__hc: http://{{ .Values.app.svc.webshoppingapigw }}/hc
+ name__apigwwm__hc: Web Marketing API GW HTTP Check
+ internalurls__apigwwm__hc: http://{{ .Values.app.svc.webmarketingapigw }}/hc
+ name__apigwms__hc: Mobile Shopping API GW HTTP Check
+ internalurls__apigwms__hc: http://{{ .Values.app.svc.mobileshoppingapigw }}/hc
+ name__apigwmm__hc: Mobile Marketing API GW HTTP Check
+ internalurls__apigwmm__hc: http://{{ .Values.app.svc.mobilemarketingapigw }}/hc
+ name__apigwwsagg__hc: Web Shopping Aggregator GW HTTP Check
+ internalurls__apigwwsagg__hc: http://{{ .Values.app.svc.webshoppingagg }}/hc
+ name__apigwmsagg__hc: Mobile Shopping Aggregator HTTP Check
+ internalurls__apigwmsagg__hc: http://{{ .Values.app.svc.mobileshoppingagg }}/hc
+ name__ordering__hc: Ordering HTTP Check
+ internalurls__ordering__hc: http://{{ .Values.app.svc.ordering }}/hc
+ name__orderingbackground__hc: Ordering HTTP Background Check
+ internalurls__orderingbackground__hc: http://{{ .Values.app.svc.orderingbackgroundtasks }}/hc
+ name__basket__hc: Basket HTTP Check
+ internalurls__basket__hc: http://{{ .Values.app.svc.basket }}/hc
+ name__catalog__hc: Catalog HTTP Check
+ internalurls__catalog__hc: http://{{ .Values.app.svc.catalog }}/hc
+ name__identity__hc: Identity HTTP Check
+ internalurls__identity__hc: http://{{ .Values.app.svc.identity }}/hc
+ name__marketing__hc: Marketing HTTP Check
+ internalurls__marketing__hc: http://{{ .Values.app.svc.marketing }}/hc
+ name__locations__hc: Locations HTTP Check
+ internalurls__locations__hc: http://{{ .Values.app.svc.locations }}/hc
+ name__payment__hc: Payment HTTP Check
+ internalurls__payment__hc: http://{{ .Values.app.svc.payment }}/hc
+ name__signalrhub__hc: Ordering SignalR Hub HTTP Check
+ internalurls__signalrhub__hc: http://{{ .Values.app.svc.orderingsignalrhub }}/hc
diff --git a/k8s/helm/webstatus/values.yaml b/k8s/helm/webstatus/values.yaml
index 8e5912fda..88cae5d7f 100644
--- a/k8s/helm/webstatus/values.yaml
+++ b/k8s/helm/webstatus/values.yaml
@@ -13,7 +13,9 @@ service:
ingress:
enabled: true
- annotations: {}
+ annotations: {
+
+ }
tls: []
resources: {}
@@ -32,27 +34,73 @@ env:
configmap:
- name: ApplicationInsights__InstrumentationKey
key: all__InstrumentationKey
- - name: BasketUrl
- key: internalurls__basket__hc
- - name: CatalogUrl
- key: internalurls__catalog__hc
- - name: IdentityUrl
- key: internalurls__identity__hc
- - name: OrderingUrl
- key: internalurls__ordering__hc
- - name: OrderingBackgroundTasksUrl
- key: internalurls__orderingbackground__hc
- - name: LocationsUrl
- key: internalurls__locations__hc
- - name: MarketingUrl
- key: internalurls__marketing__hc
- - name: IdentityUrlHC
- key: internalurls__identity__hc
- - name: mvc
+ - name: HealthChecks-UI__HealthChecks__0__Name
+ key: name__mvc__hc
+ - name: HealthChecks-UI__HealthChecks__0__Uri
key: internalurls__mvc__hc
- - name: spa
+ - name: HealthChecks-UI__HealthChecks__1__Name
+ key: name__spa__hc
+ - name: HealthChecks-UI__HealthChecks__1__Uri
key: internalurls__spa__hc
- - name: PaymentUrl
+ - name: HealthChecks-UI__HealthChecks__2__Name
+ key: name__apigwws__hc
+ - name: HealthChecks-UI__HealthChecks__2__Uri
+ key: internalurls__apigwws__hc
+ - name: HealthChecks-UI__HealthChecks__3__Name
+ key: name__apigwwm__hc
+ - name: HealthChecks-UI__HealthChecks__3__Uri
+ key: internalurls__apigwwm__hc
+ - name: HealthChecks-UI__HealthChecks__4__Name
+ key: name__apigwms__hc
+ - name: HealthChecks-UI__HealthChecks__4__Uri
+ key: internalurls__apigwms__hc
+ - name: HealthChecks-UI__HealthChecks__5__Name
+ key: name__apigwmm__hc
+ - name: HealthChecks-UI__HealthChecks__5__Uri
+ key: internalurls__apigwmm__hc
+ - name: HealthChecks-UI__HealthChecks__6__Name
+ key: name__apigwwsagg__hc
+ - name: HealthChecks-UI__HealthChecks__6__Uri
+ key: internalurls__apigwwsagg__hc
+ - name: HealthChecks-UI__HealthChecks__7__Name
+ key: name__apigwmsagg__hc
+ - name: HealthChecks-UI__HealthChecks__7__Uri
+ key: internalurls__apigwmsagg__hc
+ - name: HealthChecks-UI__HealthChecks__8__Name
+ key: name__ordering__hc
+ - name: HealthChecks-UI__HealthChecks__8__Uri
+ key: internalurls__ordering__hc
+ - name: HealthChecks-UI__HealthChecks__9__Name
+ key: name__orderingbackground__hc
+ - name: HealthChecks-UI__HealthChecks__9__Uri
+ key: internalurls__orderingbackground__hc
+ - name: HealthChecks-UI__HealthChecks__10__Name
+ key: name__signalrhub__hc
+ - name: HealthChecks-UI__HealthChecks__10__Uri
+ key: internalurls__signalrhub__hc
+ - name: HealthChecks-UI__HealthChecks__11__Name
+ key: name__basket__hc
+ - name: HealthChecks-UI__HealthChecks__11__Uri
+ key: internalurls__basket__hc
+ - name: HealthChecks-UI__HealthChecks__12__Name
+ key: name__catalog__hc
+ - name: HealthChecks-UI__HealthChecks__12__Uri
+ key: internalurls__catalog__hc
+ - name: HealthChecks-UI__HealthChecks__13__Name
+ key: name__identity__hc
+ - name: HealthChecks-UI__HealthChecks__13__Uri
+ key: internalurls__identity__hc
+ - name: HealthChecks-UI__HealthChecks__14__Name
+ key: name__marketing__hc
+ - name: HealthChecks-UI__HealthChecks__14__Uri
+ key: internalurls__marketing__hc
+ - name: HealthChecks-UI__HealthChecks__15__Name
+ key: name__locations__hc
+ - name: HealthChecks-UI__HealthChecks__15__Uri
+ key: internalurls__locations__hc
+ - name: HealthChecks-UI__HealthChecks__16__Name
+ key: name__payment__hc
+ - name: HealthChecks-UI__HealthChecks__16__Uri
key: internalurls__payment__hc
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
diff --git a/k8s/nginx-ingress/azure/service.yaml b/k8s/nginx-ingress/azure/service.yaml
deleted file mode 100644
index 8d2f71505..000000000
--- a/k8s/nginx-ingress/azure/service.yaml
+++ /dev/null
@@ -1,19 +0,0 @@
-kind: Service
-apiVersion: v1
-metadata:
- name: ingress-nginx
- namespace: ingress-nginx
- labels:
- app: ingress-nginx
-spec:
- externalTrafficPolicy: Local
- type: LoadBalancer
- selector:
- app: ingress-nginx
- ports:
- - name: http
- port: 80
- targetPort: http
- - name: https
- port: 443
- targetPort: https
diff --git a/k8s/nginx-ingress/cloud-generic.yaml b/k8s/nginx-ingress/cloud-generic.yaml
new file mode 100644
index 000000000..945441ab8
--- /dev/null
+++ b/k8s/nginx-ingress/cloud-generic.yaml
@@ -0,0 +1,21 @@
+kind: Service
+apiVersion: v1
+metadata:
+ name: ingress-nginx
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+spec:
+ externalTrafficPolicy: Local
+ type: LoadBalancer
+ selector:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+ ports:
+ - name: http
+ port: 80
+ targetPort: http
+ - name: https
+ port: 443
+ targetPort: https
\ No newline at end of file
diff --git a/k8s/nginx-ingress/cm.yaml b/k8s/nginx-ingress/cm.yaml
new file mode 100644
index 000000000..7818fd15b
Binary files /dev/null and b/k8s/nginx-ingress/cm.yaml differ
diff --git a/k8s/nginx-ingress/configmap.yaml b/k8s/nginx-ingress/configmap.yaml
deleted file mode 100644
index 6703fc38e..000000000
--- a/k8s/nginx-ingress/configmap.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-kind: ConfigMap
-apiVersion: v1
-metadata:
- name: nginx-configuration
- namespace: ingress-nginx
- labels:
- app: ingress-nginx
-data:
- ssl-redirect: "false"
- proxy-buffer-size: "128k"
- proxy-buffers: "4 256k"
diff --git a/k8s/nginx-ingress/default-backend.yaml b/k8s/nginx-ingress/default-backend.yaml
deleted file mode 100644
index 64f6f58ad..000000000
--- a/k8s/nginx-ingress/default-backend.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
- name: default-http-backend
- labels:
- app: default-http-backend
- namespace: ingress-nginx
-spec:
- replicas: 1
- template:
- metadata:
- labels:
- app: default-http-backend
- spec:
- terminationGracePeriodSeconds: 60
- containers:
- - name: default-http-backend
- # Any image is permissable as long as:
- # 1. It serves a 404 page at /
- # 2. It serves 200 on a /healthz endpoint
- image: gcr.io/google_containers/defaultbackend:1.4
- livenessProbe:
- httpGet:
- path: /healthz
- port: 8080
- scheme: HTTP
- initialDelaySeconds: 30
- timeoutSeconds: 5
- ports:
- - containerPort: 8080
- resources:
- limits:
- cpu: 10m
- memory: 20Mi
- requests:
- cpu: 10m
- memory: 20Mi
----
-
-apiVersion: v1
-kind: Service
-metadata:
- name: default-http-backend
- namespace: ingress-nginx
- labels:
- app: default-http-backend
-spec:
- ports:
- - port: 80
- targetPort: 8080
- selector:
- app: default-http-backend
diff --git a/k8s/nginx-ingress/local-dockerk8s/identityapi-cm-fix.yaml b/k8s/nginx-ingress/local-dockerk8s/identityapi-cm-fix.yaml
new file mode 100644
index 000000000..3a3fcf5a5
--- /dev/null
+++ b/k8s/nginx-ingress/local-dockerk8s/identityapi-cm-fix.yaml
@@ -0,0 +1,3 @@
+data:
+ mvc_e: http://10.0.75.1/webmvc
+
\ No newline at end of file
diff --git a/k8s/nginx-ingress/local-dockerk8s/mvc-cm-fix.yaml b/k8s/nginx-ingress/local-dockerk8s/mvc-cm-fix.yaml
new file mode 100644
index 000000000..1475deec1
--- /dev/null
+++ b/k8s/nginx-ingress/local-dockerk8s/mvc-cm-fix.yaml
@@ -0,0 +1,3 @@
+data:
+ urls__IdentityUrl: http://10.0.75.1/identity
+ urls__mvc: http://10.0.75.1/webmvc
diff --git a/k8s/nginx-ingress/local-dockerk8s/mvc-fix.yaml b/k8s/nginx-ingress/local-dockerk8s/mvc-fix.yaml
new file mode 100644
index 000000000..b9ecd4cba
--- /dev/null
+++ b/k8s/nginx-ingress/local-dockerk8s/mvc-fix.yaml
@@ -0,0 +1,39 @@
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ annotations:
+ ingress.kubernetes.io/ssl-redirect: "false"
+ kubernetes.io/ingress.class: nginx
+ nginx.ingress.kubernetes.io/ssl-redirect: "false"
+ labels:
+ app: webmvc
+ name: eshop-webmvc-loopback
+ namespace: default
+spec:
+ rules:
+ - http:
+ paths:
+ - backend:
+ serviceName: webmvc
+ servicePort: http
+ path: /webmvc
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ annotations:
+ ingress.kubernetes.io/ssl-redirect: "false"
+ kubernetes.io/ingress.class: nginx
+ nginx.ingress.kubernetes.io/ssl-redirect: "false"
+ labels:
+ app: identity-api
+ name: eshop-identity-api-loopback
+ namespace: default
+spec:
+ rules:
+ - http:
+ paths:
+ - backend:
+ serviceName: identity
+ servicePort: http
+ path: /identity
\ No newline at end of file
diff --git a/k8s/nginx-ingress/mandatory.yaml b/k8s/nginx-ingress/mandatory.yaml
new file mode 100644
index 000000000..56b1cc3b5
--- /dev/null
+++ b/k8s/nginx-ingress/mandatory.yaml
@@ -0,0 +1,238 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: ingress-nginx
+
+---
+
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: nginx-configuration
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: nginx-ingress-serviceaccount
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ name: nginx-ingress-clusterrole
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - endpoints
+ - nodes
+ - pods
+ - secrets
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - nodes
+ verbs:
+ - get
+ - apiGroups:
+ - ""
+ resources:
+ - services
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - "extensions"
+ resources:
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+ - apiGroups:
+ - "extensions"
+ resources:
+ - ingresses/status
+ verbs:
+ - update
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: Role
+metadata:
+ name: nginx-ingress-role
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - pods
+ - secrets
+ - namespaces
+ verbs:
+ - get
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ resourceNames:
+ # Defaults to "-"
+ # Here: "-"
+ # This has to be adapted if you change either parameter
+ # when launching the nginx-ingress-controller.
+ - "ingress-controller-leader-nginx"
+ verbs:
+ - get
+ - update
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - create
+ - apiGroups:
+ - ""
+ resources:
+ - endpoints
+ verbs:
+ - get
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: RoleBinding
+metadata:
+ name: nginx-ingress-role-nisa-binding
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: nginx-ingress-role
+subjects:
+ - kind: ServiceAccount
+ name: nginx-ingress-serviceaccount
+ namespace: ingress-nginx
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: nginx-ingress-clusterrole-nisa-binding
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: nginx-ingress-clusterrole
+subjects:
+ - kind: ServiceAccount
+ name: nginx-ingress-serviceaccount
+ namespace: ingress-nginx
+
+---
+
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: nginx-ingress-controller
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+ annotations:
+ prometheus.io/port: "10254"
+ prometheus.io/scrape: "true"
+ spec:
+ serviceAccountName: nginx-ingress-serviceaccount
+ containers:
+ - name: nginx-ingress-controller
+ image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.20.0
+ args:
+ - /nginx-ingress-controller
+ - --configmap=$(POD_NAMESPACE)/nginx-configuration
+ - --publish-service=$(POD_NAMESPACE)/ingress-nginx
+ - --annotations-prefix=nginx.ingress.kubernetes.io
+ securityContext:
+ capabilities:
+ drop:
+ - ALL
+ add:
+ - NET_BIND_SERVICE
+ # www-data -> 33
+ runAsUser: 33
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ ports:
+ - name: http
+ containerPort: 80
+ - name: https
+ containerPort: 443
+ livenessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
diff --git a/k8s/nginx-ingress/namespace.yaml b/k8s/nginx-ingress/namespace.yaml
deleted file mode 100644
index 6878f0be8..000000000
--- a/k8s/nginx-ingress/namespace.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-apiVersion: v1
-kind: Namespace
-metadata:
- name: ingress-nginx
diff --git a/k8s/nginx-ingress/patch-service-without-rbac.yaml b/k8s/nginx-ingress/patch-service-without-rbac.yaml
deleted file mode 100644
index 919efc389..000000000
--- a/k8s/nginx-ingress/patch-service-without-rbac.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
- name: nginx-ingress-controller
- namespace: ingress-nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: ingress-nginx
- template:
- metadata:
- labels:
- app: ingress-nginx
- spec:
- containers:
- - name: nginx-ingress-controller
- image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.9.0
- args:
- - /nginx-ingress-controller
- - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
- - --configmap=$(POD_NAMESPACE)/nginx-configuration
- - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- - --publish-service=$(POD_NAMESPACE)/ingress-nginx
- - --annotations-prefix=nginx.ingress.kubernetes.io
- env:
- - name: POD_NAME
- valueFrom:
- fieldRef:
- fieldPath: metadata.name
- - name: POD_NAMESPACE
- valueFrom:
- fieldRef:
- fieldPath: metadata.namespace
- ports:
- - name: http
- containerPort: 80
- - name: https
- containerPort: 443
diff --git a/k8s/nginx-ingress/publish-service-patch.yaml b/k8s/nginx-ingress/publish-service-patch.yaml
deleted file mode 100644
index f8f52f772..000000000
--- a/k8s/nginx-ingress/publish-service-patch.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-[
- {
- 'op': 'add',
- 'path': '/spec/template/spec/containers/0/args/-',
- 'value': '--publish-service=$(POD_NAMESPACE)/ingress-nginx'
- }
-]
diff --git a/k8s/nginx-ingress/service-nodeport.yaml b/k8s/nginx-ingress/service-nodeport.yaml
new file mode 100644
index 000000000..dd82ed3ed
--- /dev/null
+++ b/k8s/nginx-ingress/service-nodeport.yaml
@@ -0,0 +1,22 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: ingress-nginx
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+spec:
+ type: NodePort
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
+ protocol: TCP
+ - name: https
+ port: 443
+ targetPort: 443
+ protocol: TCP
+ selector:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
diff --git a/k8s/nginx-ingress/tcp-services-configmap.yaml b/k8s/nginx-ingress/tcp-services-configmap.yaml
deleted file mode 100644
index a963085d3..000000000
--- a/k8s/nginx-ingress/tcp-services-configmap.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-kind: ConfigMap
-apiVersion: v1
-metadata:
- name: tcp-services
- namespace: ingress-nginx
diff --git a/k8s/nginx-ingress/udp-services-configmap.yaml b/k8s/nginx-ingress/udp-services-configmap.yaml
deleted file mode 100644
index 1870931a2..000000000
--- a/k8s/nginx-ingress/udp-services-configmap.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-kind: ConfigMap
-apiVersion: v1
-metadata:
- name: udp-services
- namespace: ingress-nginx
diff --git a/k8s/nginx-ingress/without-rbac.yaml b/k8s/nginx-ingress/without-rbac.yaml
deleted file mode 100644
index 1c46b73eb..000000000
--- a/k8s/nginx-ingress/without-rbac.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
- name: nginx-ingress-controller
- namespace: ingress-nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: ingress-nginx
- template:
- metadata:
- labels:
- app: ingress-nginx
- annotations:
- prometheus.io/port: '10254'
- prometheus.io/scrape: 'true'
- spec:
- containers:
- - name: nginx-ingress-controller
- image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.9.0
- args:
- - /nginx-ingress-controller
- - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
- - --configmap=$(POD_NAMESPACE)/nginx-configuration
- - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- - --annotations-prefix=nginx.ingress.kubernetes.io
- env:
- - name: POD_NAME
- valueFrom:
- fieldRef:
- fieldPath: metadata.name
- - name: POD_NAMESPACE
- valueFrom:
- fieldRef:
- fieldPath: metadata.namespace
- ports:
- - name: http
- containerPort: 80
- - name: https
- containerPort: 443
- livenessProbe:
- failureThreshold: 3
- httpGet:
- path: /healthz
- port: 10254
- scheme: HTTP
- initialDelaySeconds: 10
- periodSeconds: 10
- successThreshold: 1
- timeoutSeconds: 1
- readinessProbe:
- failureThreshold: 3
- httpGet:
- path: /healthz
- port: 10254
- scheme: HTTP
- periodSeconds: 10
- successThreshold: 1
- timeoutSeconds: 1
diff --git a/src/ApiGateways/ApiGw-Base/Dockerfile b/src/ApiGateways/ApiGw-Base/Dockerfile
index 7fdd5f073..eb94675b9 100644
--- a/src/ApiGateways/ApiGw-Base/Dockerfile
+++ b/src/ApiGateways/ApiGw-Base/Dockerfile
@@ -1,13 +1,12 @@
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
-FROM microsoft/dotnet:2.1-sdk AS build
+FROM microsoft/dotnet:2.2.100-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 d3b1a049b..eb88972c0 100644
--- a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj
+++ b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj
@@ -1,15 +1,17 @@
- netcoreapp2.1
+ netcoreapp2.2
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/ApiGateways/ApiGw-Base/Program.cs b/src/ApiGateways/ApiGw-Base/Program.cs
index effd5684e..bcf1c8d60 100644
--- a/src/ApiGateways/ApiGw-Base/Program.cs
+++ b/src/ApiGateways/ApiGw-Base/Program.cs
@@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
+using Serilog;
namespace OcelotApiGw
{
@@ -23,7 +24,20 @@ namespace OcelotApiGw
IWebHostBuilder builder = WebHost.CreateDefaultBuilder(args);
builder.ConfigureServices(s => s.AddSingleton(builder))
.ConfigureAppConfiguration(ic => ic.AddJsonFile(Path.Combine("configuration", "configuration.json")))
- .UseStartup();
+ .UseStartup()
+ .ConfigureLogging((hostingContext, loggingbuilder) =>
+ {
+ loggingbuilder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ loggingbuilder.AddConsole();
+ loggingbuilder.AddDebug();
+ })
+ .UseSerilog((builderContext, config) =>
+ {
+ config
+ .MinimumLevel.Information()
+ .Enrich.FromLogContext()
+ .WriteTo.Console();
+ });
IWebHost host = builder.Build();
return host;
}
diff --git a/src/ApiGateways/ApiGw-Base/Startup.cs b/src/ApiGateways/ApiGw-Base/Startup.cs
index f6a36b59e..585c26471 100644
--- a/src/ApiGateways/ApiGw-Base/Startup.cs
+++ b/src/ApiGateways/ApiGw-Base/Startup.cs
@@ -1,22 +1,19 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CacheManager.Core;
-using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
+using System;
+using HealthChecks.UI.Client;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace OcelotApiGw
{
public class Startup
{
-
private readonly IConfiguration _cfg;
public Startup(IConfiguration configuration)
@@ -29,12 +26,23 @@ namespace OcelotApiGw
var identityUrl = _cfg.GetValue("IdentityUrl");
var authenticationProviderKey = "IdentityApiKey";
+ services.AddHealthChecks()
+ .AddCheck("self", () => HealthCheckResult.Healthy())
+ .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",
- builder => builder.AllowAnyOrigin()
+ builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
+ .SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
@@ -68,9 +76,10 @@ namespace OcelotApiGw
services.AddOcelot(_cfg);
}
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var pathBase = _cfg["PATH_BASE"];
+
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
@@ -81,7 +90,16 @@ namespace OcelotApiGw
app.UseDeveloperExceptionPage();
}
- loggerFactory.AddConsole(_cfg.GetSection("Logging"));
+ app.UseHealthChecks("/hc", new HealthCheckOptions()
+ {
+ Predicate = _ => true,
+ ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
+ });
+
+ app.UseHealthChecks("/liveness", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Contains("self")
+ });
app.UseCors("CorsPolicy");
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs
index 702c805fb..978b1858c 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs
@@ -5,16 +5,19 @@ using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
- public class BasketController : Controller
+ [ApiController]
+ public class BasketController : ControllerBase
{
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
+
public BasketController(ICatalogService catalogService, IBasketService basketService)
{
_catalog = catalogService;
@@ -23,22 +26,24 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[HttpPost]
[HttpPut]
- public async Task UpdateAllBasket([FromBody] UpdateBasketRequest data)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
+ public async Task> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
-
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
- var currentBasket = await _basket.GetById(data.BuyerId);
+ var currentBasket = await _basket.GetByIdAsync(data.BuyerId);
+
if (currentBasket == null)
{
currentBasket = new BasketData(data.BuyerId);
}
- var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId));
+ var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
var newBasket = new BasketData(data.BuyerId);
foreach (var bitem in data.Items)
@@ -60,13 +65,16 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
});
}
- await _basket.Update(newBasket);
- return Ok(newBasket);
+ await _basket.UpdateAsync(newBasket);
+
+ return newBasket;
}
[HttpPut]
[Route("items")]
- public async Task UpdateQuantities([FromBody] UpdateBasketItemsRequest data)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
+ public async Task> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
@@ -74,7 +82,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
}
// Retrieve the current basket
- var currentBasket = await _basket.GetById(data.BasketId);
+ var currentBasket = await _basket.GetByIdAsync(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
@@ -84,21 +92,26 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
+
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
+
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
- await _basket.Update(currentBasket);
- return Ok(currentBasket);
+ await _basket.UpdateAsync(currentBasket);
+
+ return currentBasket;
}
[HttpPost]
[Route("items")]
- public async Task AddBasketItem([FromBody] AddBasketItemRequest data)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType((int)HttpStatusCode.OK)]
+ public async Task AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
@@ -106,12 +119,12 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
}
// Step 1: Get the item from catalog
- var item = await _catalog.GetCatalogItem(data.CatalogItemId);
+ var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
- var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
+ var currentBasket = (await _basket.GetByIdAsync(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
@@ -124,8 +137,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
});
// Step 4: Update basket
- await _basket.Update(currentBasket);
-
+ await _basket.UpdateAsync(currentBasket);
return Ok();
}
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs
index 4c18d25ae..a4b33c6cb 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs
@@ -1,19 +1,20 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
- public class OrderController : Controller
+ [ApiController]
+ public class OrderController : ControllerBase
{
private readonly IBasketService _basketService;
private readonly IOrderApiClient _orderClient;
+
public OrderController(IBasketService basketService, IOrderApiClient orderClient)
{
_basketService = basketService;
@@ -22,21 +23,23 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[Route("draft/{basketId}")]
[HttpGet]
- public async Task GetOrderDraft(string basketId)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
+ public async Task> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
- var basket = await _basketService.GetById(basketId);
+ var basket = await _basketService.GetByIdAsync(basketId);
+
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
- var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket);
- return Ok(orderDraft);
+ return await _orderClient.GetOrderDraftFromBasketAsync(basket);
}
}
}
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
index 7787dd159..be89c315f 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
@@ -1,8 +1,8 @@
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
-FROM microsoft/dotnet:2.1-sdk AS build
+FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator
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 d49c6a18f..1e9508843 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp2.2
Mobile.Shopping.HttpAggregator
Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
..\..\..\docker-compose.dcproj
@@ -12,9 +12,15 @@
+
+
-
-
+
+
+
+
+
+
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs
index 0c88fcd7d..fc21a70a4 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using Serilog;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
@@ -31,6 +32,13 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
});
})
.UseStartup()
+ .UseSerilog((builderContext, config) =>
+ {
+ config
+ .MinimumLevel.Information()
+ .Enrich.FromLogContext()
+ .WriteTo.Console();
+ })
.Build();
}
}
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs
index 8339ee44b..5186fe361 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs
@@ -22,7 +22,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
_urls = config.Value;
}
- public async Task GetById(string id)
+ public async Task GetByIdAsync(string id)
{
var data = await _httpClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id));
@@ -31,7 +31,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
return basket;
}
- public async Task Update(BasketData currentBasket)
+ public async Task UpdateAsync(BasketData currentBasket)
{
var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json");
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs
index 6c59f0c49..69bca2b39 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs
@@ -22,7 +22,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
_urls = config.Value;
}
- public async Task GetCatalogItem(int id)
+ public async Task GetCatalogItemAsync(int id)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id));
var catalogItem = JsonConvert.DeserializeObject(stringContent);
@@ -30,7 +30,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
return catalogItem;
}
- public async Task> GetCatalogItems(IEnumerable ids)
+ public async Task> GetCatalogItemsAsync(IEnumerable ids)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids));
var catalogItems = JsonConvert.DeserializeObject(stringContent);
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs
index 6fd6871c1..ad49e1adb 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs
@@ -1,15 +1,13 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface IBasketService
{
- Task GetById(string id);
- Task Update(BasketData currentBasket);
+ Task GetByIdAsync(string id);
+
+ Task UpdateAsync(BasketData currentBasket);
}
}
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs
index d7e605bfa..832dfc740 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs
@@ -1,14 +1,13 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
-using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface ICatalogService
{
- Task GetCatalogItem(int id);
- Task> GetCatalogItems(IEnumerable ids);
+ Task GetCatalogItemAsync(int id);
+
+ Task> GetCatalogItemsAsync(IEnumerable ids);
}
}
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
index 30ac013b9..1abe545bd 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
@@ -1,13 +1,10 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface IOrderApiClient
{
- Task GetOrderDraftFromBasket(BasketData basket);
+ Task GetOrderDraftFromBasketAsync(BasketData basket);
}
}
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs
index 03644c110..da39abbff 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs
@@ -21,7 +21,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
_urls = config.Value;
}
- public async Task GetOrderDraftFromBasket(BasketData basket)
+ public async Task GetOrderDraftFromBasketAsync(BasketData basket)
{
var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs
index 1d24e8312..924b5b1aa 100644
--- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs
+++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs
@@ -1,7 +1,12 @@
-using Microsoft.AspNetCore.Authentication.JwtBearer;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Net.Http;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
@@ -12,10 +17,9 @@ using Microsoft.Extensions.Logging;
using Polly;
using Polly.Extensions.Http;
using Swashbuckle.AspNetCore.Swagger;
-using System;
-using System.Collections.Generic;
-using System.IdentityModel.Tokens.Jwt;
-using System.Net.Http;
+using HealthChecks.UI.Client;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
@@ -31,6 +35,16 @@ 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()
+ .AddCheck("self", () => HealthCheckResult.Healthy())
+ .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();
@@ -43,25 +57,45 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
if (!string.IsNullOrEmpty(pathBase))
{
- loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
+ loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
+ app.UseHealthChecks("/hc", new HealthCheckOptions()
+ {
+ Predicate = _ => true,
+ ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
+ });
+
+ app.UseHealthChecks("/liveness", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Contains("self")
+ });
+
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
+ else
+ {
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
app.UseAuthentication();
-
+ app.UseHttpsRedirection();
app.UseMvc();
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
- c.ConfigureOAuth2("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
+
+ c.OAuthClientId("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui");
+ c.OAuthClientSecret(string.Empty);
+ c.OAuthRealm(string.Empty);
+ c.OAuthAppName("Purchase BFF Swagger UI");
});
}
}
@@ -73,7 +107,8 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
services.AddOptions();
services.Configure(configuration.GetSection("urls"));
- services.AddMvc();
+ services.AddMvc()
+ .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSwaggerGen(options =>
{
@@ -104,9 +139,10 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
- builder => builder.AllowAnyOrigin()
+ builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
+ .SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
@@ -116,12 +152,14 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var identityUrl = configuration.GetValue("urls:identity");
+
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
- }).AddJwtBearer(options =>
+ })
+ .AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
@@ -130,17 +168,16 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
OnAuthenticationFailed = async ctx =>
{
- int i = 0;
},
OnTokenValidated = async ctx =>
{
- int i = 0;
}
};
});
return services;
}
+
public static IServiceCollection AddHttpServices(this IServiceCollection services)
{
//register delegating handlers
@@ -161,20 +198,18 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
-
-
return services;
}
- static IAsyncPolicy GetRetryPolicy()
+ private static IAsyncPolicy GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
-
}
- static IAsyncPolicy GetCircuitBreakerPolicy()
+
+ private static IAsyncPolicy GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs
index bfef55726..6745ffa02 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs
@@ -5,16 +5,19 @@ using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
- public class BasketController : Controller
+ [ApiController]
+ public class BasketController : ControllerBase
{
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
+
public BasketController(ICatalogService catalogService, IBasketService basketService)
{
_catalog = catalogService;
@@ -23,22 +26,23 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
[HttpPost]
[HttpPut]
- public async Task UpdateAllBasket([FromBody] UpdateBasketRequest data)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
+ public async Task> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
-
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
- var currentBasket = await _basket.GetById(data.BuyerId);
+ var currentBasket = await _basket.GetByIdAsync(data.BuyerId);
if (currentBasket == null)
{
currentBasket = new BasketData(data.BuyerId);
}
- var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId));
+ var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
var newBasket = new BasketData(data.BuyerId);
foreach (var bitem in data.Items)
@@ -60,13 +64,16 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
});
}
- await _basket.Update(newBasket);
- return Ok(newBasket);
+ await _basket.UpdateAsync(newBasket);
+
+ return newBasket;
}
[HttpPut]
[Route("items")]
- public async Task UpdateQuantities([FromBody] UpdateBasketItemsRequest data)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
+ public async Task> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
@@ -74,7 +81,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
}
// Retrieve the current basket
- var currentBasket = await _basket.GetById(data.BasketId);
+ var currentBasket = await _basket.GetByIdAsync(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
@@ -92,13 +99,16 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
}
// Save the updated basket
- await _basket.Update(currentBasket);
- return Ok(currentBasket);
+ await _basket.UpdateAsync(currentBasket);
+
+ return currentBasket;
}
[HttpPost]
[Route("items")]
- public async Task AddBasketItem([FromBody] AddBasketItemRequest data)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType((int)HttpStatusCode.OK)]
+ public async Task AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
@@ -106,12 +116,12 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
}
// Step 1: Get the item from catalog
- var item = await _catalog.GetCatalogItem(data.CatalogItemId);
+ var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
- var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
+ var currentBasket = (await _basket.GetByIdAsync(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
@@ -124,8 +134,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
});
// Step 4: Update basket
- await _basket.Update(currentBasket);
-
+ await _basket.UpdateAsync(currentBasket);
return Ok();
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs
index fd108ffb8..de3e4cc55 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs
@@ -1,16 +1,19 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
- public class OrderController : Controller
+ [ApiController]
+ public class OrderController : ControllerBase
{
private readonly IBasketService _basketService;
private readonly IOrderApiClient _orderClient;
@@ -22,21 +25,23 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
[Route("draft/{basketId}")]
[HttpGet]
- public async Task GetOrderDraft(string basketId)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ [ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
+ public async Task> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
- var basket = await _basketService.GetById(basketId);
+ var basket = await _basketService.GetByIdAsync(basketId);
+
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
- var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket);
- return Ok(orderDraft);
+ return await _orderClient.GetOrderDraftFromBasketAsync(basket);
}
}
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile
index d4940d436..236d3705a 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile
@@ -1,8 +1,8 @@
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
-FROM microsoft/dotnet:2.1-sdk AS build
+FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/ApiGateways/Web.Bff.Shopping/aggregator
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs
index c865a8b3b..4bbac21e6 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using Serilog;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
{
@@ -31,6 +32,13 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
});
})
.UseStartup()
+ .UseSerilog((builderContext, config) =>
+ {
+ config
+ .MinimumLevel.Information()
+ .Enrich.FromLogContext()
+ .WriteTo.Console();
+ })
.Build();
}
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs
index 291e98fd3..d0fb5c008 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs
@@ -10,7 +10,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public class BasketService : IBasketService
{
-
private readonly HttpClient _apiClient;
private readonly ILogger _logger;
private readonly UrlsConfig _urls;
@@ -22,18 +21,19 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
_urls = config.Value;
}
- public async Task GetById(string id)
+ public async Task GetByIdAsync(string id)
{
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id));
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject(data) : null;
+
return basket;
}
- public async Task Update(BasketData currentBasket)
+ public async Task UpdateAsync(BasketData currentBasket)
{
var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json");
- var data = await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent);
+ await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent);
}
}
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs
index ba67b7c1e..159e56d85 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs
@@ -11,7 +11,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public class CatalogService : ICatalogService
{
-
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
private readonly UrlsConfig _urls;
@@ -23,20 +22,18 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
_urls = config.Value;
}
- public async Task GetCatalogItem(int id)
+ public async Task GetCatalogItemAsync(int id)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id));
- var catalogItem = JsonConvert.DeserializeObject(stringContent);
- return catalogItem;
+ return JsonConvert.DeserializeObject(stringContent);
}
- public async Task> GetCatalogItems(IEnumerable ids)
+ public async Task> GetCatalogItemsAsync(IEnumerable ids)
{
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids));
- var catalogItems = JsonConvert.DeserializeObject(stringContent);
- return catalogItems;
+ return JsonConvert.DeserializeObject(stringContent);
}
}
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs
index f59c31965..046ef753d 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs
@@ -1,15 +1,12 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface IBasketService
{
- Task GetById(string id);
- Task Update(BasketData currentBasket);
+ Task GetByIdAsync(string id);
+ Task UpdateAsync(BasketData currentBasket);
}
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs
index 7d192f3cc..9f86b84f9 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs
@@ -1,14 +1,13 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
-using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface ICatalogService
{
- Task GetCatalogItem(int id);
- Task> GetCatalogItems(IEnumerable ids);
+ Task GetCatalogItemAsync(int id);
+
+ Task> GetCatalogItemsAsync(IEnumerable ids);
}
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
index c97eccbbd..0e972833c 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
@@ -1,13 +1,10 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface IOrderApiClient
{
- Task GetOrderDraftFromBasket(BasketData basket);
+ Task GetOrderDraftFromBasketAsync(BasketData basket);
}
}
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs
index d43e392d3..a26028d69 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs
@@ -10,7 +10,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public class OrderApiClient : IOrderApiClient
{
-
private readonly HttpClient _apiClient;
private readonly ILogger _logger;
private readonly UrlsConfig _urls;
@@ -22,7 +21,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
_urls = config.Value;
}
- public async Task GetOrderDraftFromBasket(BasketData basket)
+ public async Task GetOrderDraftFromBasketAsync(BasketData basket)
{
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs
index e4a080289..6d3da29b7 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs
@@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure;
@@ -17,6 +18,9 @@ using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
+using HealthChecks.UI.Client;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
{
@@ -32,6 +36,16 @@ 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()
+ .AddCheck("self", () => HealthCheckResult.Healthy())
+ .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();
@@ -43,28 +57,43 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
- loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
+ loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
+ app.UseHealthChecks("/hc", new HealthCheckOptions()
+ {
+ Predicate = _ => true,
+ ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
+ });
+
+ app.UseHealthChecks("/liveness", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Contains("self")
+ });
+
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
+ else
+ {
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
app.UseAuthentication();
-
+ app.UseHttpsRedirection();
app.UseMvc();
- app.UseSwagger().UseSwaggerUI(c =>
- {
- c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
- //c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
- });
-
-
+ app.UseSwagger()
+ .UseSwaggerUI(c =>
+ {
+ c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
+ //c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
+ });
}
}
@@ -99,12 +128,14 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
return services;
}
+
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure(configuration.GetSection("urls"));
- services.AddMvc();
+ services.AddMvc()
+ .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSwaggerGen(options =>
{
@@ -135,7 +166,8 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
- builder => builder.AllowAnyOrigin()
+ builder => builder
+ .SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
@@ -177,6 +209,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
+
static IAsyncPolicy GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
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 7a0f6cc01..a15e101d3 100644
--- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj
+++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp2.2
Web.Shopping.HttpAggregator
Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
..\..\..\docker-compose.dcproj
@@ -12,10 +12,14 @@
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs b/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs
index e01a7aaa8..ef09911fe 100644
--- a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs
+++ b/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs
@@ -1,4 +1,5 @@
using System;
+using Newtonsoft.Json;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events
{
@@ -10,7 +11,17 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events
CreationDate = DateTime.UtcNow;
}
- public Guid Id { get; }
- public DateTime CreationDate { get; }
+ [JsonConstructor]
+ public IntegrationEvent(Guid id, DateTime createDate)
+ {
+ Id = id;
+ CreationDate = createDate;
+ }
+
+ [JsonProperty]
+ public Guid Id { get; private set; }
+
+ [JsonProperty]
+ public DateTime CreationDate { get; private set; }
}
}
diff --git a/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs b/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs
new file mode 100644
index 000000000..de5a2cb79
--- /dev/null
+++ b/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions
+{
+ public static class GenericTypeExtensions
+ {
+ public static string GetGenericTypeName(this Type type)
+ {
+ var typeName = string.Empty;
+
+ if (type.IsGenericType)
+ {
+ var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray());
+ typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>";
+ }
+ else
+ {
+ typeName = type.Name;
+ }
+
+ return typeName;
+ }
+
+ public static string GetGenericTypeName(this object @object)
+ {
+ return @object.GetType().GetGenericTypeName();
+ }
+ }
+}
diff --git a/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs
index 88be8cf96..e789081f3 100644
--- a/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs
+++ b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs
@@ -1,10 +1,8 @@
-using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
-using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
-using System.Text;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
{
@@ -37,8 +35,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus
where TH : IIntegrationEventHandler
{
var eventName = GetEventKey();
+
DoAddSubscription(typeof(TH), eventName, isDynamic: false);
- _eventTypes.Add(typeof(T));
+
+ if (!_eventTypes.Contains(typeof(T)))
+ {
+ _eventTypes.Add(typeof(T));
+ }
}
private void DoAddSubscription(Type handlerType, string eventName, bool isDynamic)
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs
index 2e0555e61..93e5b2917 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs
@@ -72,7 +72,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
.Or()
.WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
{
- _logger.LogWarning(ex.ToString());
+ _logger.LogWarning(ex, "RabbitMQ Client could not connect after {TimeOut}s ({ExceptionMessage})", $"{time.TotalSeconds:n1}", ex.Message);
}
);
@@ -88,7 +88,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
_connection.CallbackException += OnCallbackException;
_connection.ConnectionBlocked += OnConnectionBlocked;
- _logger.LogInformation($"RabbitMQ persistent connection acquired a connection {_connection.Endpoint.HostName} and is subscribed to failure events");
+ _logger.LogInformation("RabbitMQ Client acquired a persistent connection to '{HostName}' and is subscribed to failure events", _connection.Endpoint.HostName);
return true;
}
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
index 49a417635..ac379d50a 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
@@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -76,7 +77,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
.Or()
.WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
{
- _logger.LogWarning(ex.ToString());
+ _logger.LogWarning(ex, "Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id, $"{time.TotalSeconds:n1}", ex.Message);
});
using (var channel = _persistentConnection.CreateModel())
@@ -107,6 +108,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
public void SubscribeDynamic(string eventName)
where TH : IDynamicIntegrationEventHandler
{
+ _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
+
DoInternalSubscription(eventName);
_subsManager.AddDynamicSubscription | (eventName);
}
@@ -117,6 +120,9 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
{
var eventName = _subsManager.GetEventKey();
DoInternalSubscription(eventName);
+
+ _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
+
_subsManager.AddSubscription();
}
@@ -140,9 +146,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
}
public void Unsubscribe()
- where TH : IIntegrationEventHandler
where T : IntegrationEvent
+ where TH : IIntegrationEventHandler
{
+ var eventName = _subsManager.GetEventKey();
+
+ _logger.LogInformation("Unsubscribing from event {EventName}", eventName);
+
_subsManager.RemoveSubscription();
}
@@ -215,16 +225,18 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
foreach (var subscription in subscriptions)
{
if (subscription.IsDynamic)
- {
+ {
var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;
+ if (handler == null) continue;
dynamic eventData = JObject.Parse(message);
await handler.Handle(eventData);
}
else
{
+ var handler = scope.ResolveOptional(subscription.HandlerType);
+ if (handler == null) continue;
var eventType = _subsManager.GetEventTypeByName(eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
- var handler = scope.ResolveOptional(subscription.HandlerType);
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
}
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj
index 06514ba41..62373d1b3 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs
index 2cd86669b..cd2dc557f 100644
--- a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs
+++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs
@@ -27,7 +27,7 @@
ILifetimeScope autofac)
{
_serviceBusPersisterConnection = serviceBusPersisterConnection;
- _logger = logger;
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
_subscriptionClient = new SubscriptionClient(serviceBusPersisterConnection.ServiceBusConnectionStringBuilder,
@@ -61,6 +61,8 @@
public void SubscribeDynamic(string eventName)
where TH : IDynamicIntegrationEventHandler
{
+ _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, nameof(TH));
+
_subsManager.AddDynamicSubscription | (eventName);
}
@@ -83,10 +85,12 @@
}
catch (ServiceBusException)
{
- _logger.LogInformation($"The messaging entity {eventName} already exists.");
+ _logger.LogWarning("The messaging entity {eventName} already exists.", eventName);
}
}
+ _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, nameof(TH));
+
_subsManager.AddSubscription();
}
@@ -105,15 +109,19 @@
}
catch (MessagingEntityNotFoundException)
{
- _logger.LogInformation($"The messaging entity {eventName} Could not be found.");
+ _logger.LogWarning("The messaging entity {eventName} Could not be found.", eventName);
}
+ _logger.LogInformation("Unsubscribing from event {EventName}", eventName);
+
_subsManager.RemoveSubscription();
}
public void UnsubscribeDynamic(string eventName)
where TH : IDynamicIntegrationEventHandler
{
+ _logger.LogInformation("Unsubscribing from dynamic event {EventName}", eventName);
+
_subsManager.RemoveDynamicSubscription | (eventName);
}
@@ -136,17 +144,16 @@
await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}
},
- new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 10, AutoComplete = false });
+ new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 10, AutoComplete = false });
}
private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
- Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
+ var ex = exceptionReceivedEventArgs.Exception;
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
- Console.WriteLine("Exception context for troubleshooting:");
- Console.WriteLine($"- Endpoint: {context.Endpoint}");
- Console.WriteLine($"- Entity Path: {context.EntityPath}");
- Console.WriteLine($"- Executing Action: {context.Action}");
+
+ _logger.LogError(ex, "ERROR handling message: {ExceptionMessage} - Context: {@ExceptionContext}", ex.Message, context);
+
return Task.CompletedTask;
}
@@ -163,14 +170,16 @@
if (subscription.IsDynamic)
{
var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;
+ if (handler == null) continue;
dynamic eventData = JObject.Parse(message);
await handler.Handle(eventData);
}
else
{
+ var handler = scope.ResolveOptional(subscription.HandlerType);
+ if (handler == null) continue;
var eventType = _subsManager.GetEventTypeByName(eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
- var handler = scope.ResolveOptional(subscription.HandlerType);
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
}
@@ -192,7 +201,7 @@
}
catch (MessagingEntityNotFoundException)
{
- _logger.LogInformation($"The messaging entity { RuleDescription.DefaultRuleName } Could not be found.");
+ _logger.LogWarning("The messaging entity {DefaultRuleName} Could not be found.", RuleDescription.DefaultRuleName);
}
}
}
diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj
index 9eb4bd19a..3de07e329 100644
--- a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj
+++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs
index 3efb78e74..079cf7d7e 100644
--- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs
+++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs
@@ -7,7 +7,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
public enum EventStateEnum
{
NotPublished = 0,
- Published = 1,
- PublishedFailed = 2
+ InProgress = 1,
+ Published = 2,
+ PublishedFailed = 3
}
}
diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj
index 5ffc3c9eb..ef3463cca 100644
--- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj
+++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj
@@ -6,10 +6,10 @@
-
-
-
-
+
+
+
+
diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs
index 3cab9e500..e5c3bc9ad 100644
--- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs
+++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs
@@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+using System.Linq;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Reflection;
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
{
@@ -11,7 +14,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
private IntegrationEventLogEntry() { }
public IntegrationEventLogEntry(IntegrationEvent @event)
{
- EventId = @event.Id;
+ EventId = @event.Id;
CreationTime = @event.CreationDate;
EventTypeName = @event.GetType().FullName;
Content = JsonConvert.SerializeObject(@event);
@@ -20,9 +23,19 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
}
public Guid EventId { get; private set; }
public string EventTypeName { get; private set; }
+ [NotMapped]
+ public string EventTypeShortName => EventTypeName.Split('.')?.Last();
+ [NotMapped]
+ public IntegrationEvent IntegrationEvent { get; private set; }
public EventStateEnum State { get; set; }
public int TimesSent { get; set; }
public DateTime CreationTime { get; private set; }
public string Content { get; private set; }
+
+ public IntegrationEventLogEntry DeserializeJsonContent(Type type)
+ {
+ IntegrationEvent = JsonConvert.DeserializeObject(Content, type) as IntegrationEvent;
+ return this;
+ }
}
}
diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs
index ed1f74616..6167d8ae8 100644
--- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs
+++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs
@@ -9,7 +9,10 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Servi
{
public interface IIntegrationEventLogService
{
+ Task> RetrieveEventLogsPendingToPublishAsync();
Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction);
- Task MarkEventAsPublishedAsync(IntegrationEvent @event);
+ Task MarkEventAsPublishedAsync(Guid eventId);
+ Task MarkEventAsInProgressAsync(Guid eventId);
+ Task MarkEventAsFailedAsync(Guid eventId);
}
}
diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs
index a12309482..2712c5e1c 100644
--- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs
+++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs
@@ -1,9 +1,14 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
+using Newtonsoft.Json;
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
+using System.Reflection;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services
@@ -12,6 +17,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Servi
{
private readonly IntegrationEventLogContext _integrationEventLogContext;
private readonly DbConnection _dbConnection;
+ private readonly List _eventTypes;
public IntegrationEventLogService(DbConnection dbConnection)
{
@@ -21,6 +27,20 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Servi
.UseSqlServer(_dbConnection)
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning))
.Options);
+
+ _eventTypes = Assembly.Load(Assembly.GetEntryAssembly().FullName)
+ .GetTypes()
+ .Where(t => t.Name.EndsWith(nameof(IntegrationEvent)))
+ .ToList();
+ }
+
+ public async Task> RetrieveEventLogsPendingToPublishAsync()
+ {
+ return await _integrationEventLogContext.IntegrationEventLogs
+ .Where(e => e.State == EventStateEnum.NotPublished)
+ .OrderBy(o => o.CreationTime)
+ .Select(e => e.DeserializeJsonContent(_eventTypes.Find(t=> t.Name == e.EventTypeShortName)))
+ .ToListAsync();
}
public Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction)
@@ -38,11 +58,28 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Servi
return _integrationEventLogContext.SaveChangesAsync();
}
- public Task MarkEventAsPublishedAsync(IntegrationEvent @event)
+ public Task MarkEventAsPublishedAsync(Guid eventId)
{
- var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == @event.Id);
- eventLogEntry.TimesSent++;
- eventLogEntry.State = EventStateEnum.Published;
+ return UpdateEventStatus(eventId, EventStateEnum.Published);
+ }
+
+ public Task MarkEventAsInProgressAsync(Guid eventId)
+ {
+ return UpdateEventStatus(eventId, EventStateEnum.InProgress);
+ }
+
+ public Task MarkEventAsFailedAsync(Guid eventId)
+ {
+ return UpdateEventStatus(eventId, EventStateEnum.PublishedFailed);
+ }
+
+ private Task UpdateEventStatus(Guid eventId, EventStateEnum status)
+ {
+ var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == eventId);
+ eventLogEntry.State = status;
+
+ if(status == EventStateEnum.InProgress)
+ eventLogEntry.TimesSent++;
_integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry);
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 ce659345f..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 844098052..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 6217f6069..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 5e63cac75..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/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj
index ad891e6bd..1d4ea7a2d 100644
--- a/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj
+++ b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj
@@ -1,12 +1,12 @@
- netcoreapp2.1
+ netcoreapp2.2
-
+
diff --git a/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs
index ef80f77cf..fa06e0a0b 100644
--- a/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs
+++ b/src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Polly;
@@ -10,8 +11,17 @@ namespace Microsoft.AspNetCore.Hosting
{
public static class IWebHostExtensions
{
+ public static bool IsInKubernetes(this IWebHost webHost)
+ {
+ var cfg = webHost.Services.GetService();
+ var orchestratorType = cfg.GetValue("OrchestratorType");
+ return orchestratorType?.ToUpper() == "K8S";
+ }
+
public static IWebHost MigrateDbContext(this IWebHost webHost, Action seeder) where TContext : DbContext
{
+ var underK8s = webHost.IsInKubernetes();
+
using (var scope = webHost.Services.CreateScope())
{
var services = scope.ServiceProvider;
@@ -22,38 +32,49 @@ namespace Microsoft.AspNetCore.Hosting
try
{
- logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}");
+ logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name);
- var retry = Policy.Handle()
- .WaitAndRetry(new TimeSpan[]
- {
+ if (underK8s)
+ {
+ InvokeSeeder(seeder, context, services);
+ }
+ else
+ {
+ var retry = Policy.Handle()
+ .WaitAndRetry(new TimeSpan[]
+ {
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(8),
- });
+ });
- retry.Execute(() =>
- {
//if the sql server container is not created on run docker compose this
//migration can't fail for network related exception. The retry options for DbContext only
- //apply to transient exceptions.
+ //apply to transient exceptions
+ // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service)
+ retry.Execute(() => InvokeSeeder(seeder, context, services));
+ }
- context.Database
- .Migrate();
-
- seeder(context, services);
- });
-
-
- logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}");
+ logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name);
}
catch (Exception ex)
{
- logger.LogError(ex, $"An error occurred while migrating the database used on context {typeof(TContext).Name}");
+ logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name);
+ if (underK8s)
+ {
+ throw; // Rethrow under k8s because we rely on k8s to re-run the pod
+ }
}
}
return webHost;
}
+
+ private static void InvokeSeeder(Action seeder, TContext context, IServiceProvider services)
+ where TContext : DbContext
+ {
+ context.Database.Migrate();
+ seeder(context, services);
+ }
}
}
diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak
deleted file mode 100644
index 0d45fd280..000000000
--- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak
+++ /dev/null
@@ -1,175 +0,0 @@
-
-
-
- Debug
- iPhoneSimulator
- {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}
- {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- Exe
- eShopOnContainers.TestRunner.iOS
- Resources
- eShopOnContainers.TestRunner.iOS
-
-
-
-
- true
- full
- false
- bin\iPhoneSimulator\Debug
- DEBUG
- prompt
- 4
- false
- x86_64
- SdkOnly
- True
- 10.1
- False
- False
- False
- False
- False
- False
- False
- True
- Default
- HttpClientHandler
- False
-
-
- none
- true
- bin\iPhoneSimulator\Release
- prompt
- 4
- None
- x86_64
- false
-
-
- true
- full
- false
- bin\iPhone\Debug
- DEBUG
- prompt
- 4
- false
- ARMv7, ARM64
- Entitlements.plist
- iPhone Developer
- true
-
-
- none
- true
- bin\iPhone\Release
- prompt
- 4
- Entitlements.plist
- ARMv7, ARM64
- false
- iPhone Developer
-
-
- none
- True
- bin\iPhone\Ad-Hoc
- prompt
- 4
- False
- ARMv7, ARM64
- Entitlements.plist
- True
- Automatic:AdHoc
- iPhone Distribution
-
-
- none
- True
- bin\iPhone\AppStore
- prompt
- 4
- False
- ARMv7, ARM64
- Entitlements.plist
- Automatic:AppStore
- iPhone Distribution
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll
- True
-
-
- ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll
- True
-
-
- ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll
- True
-
-
- ..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll
- True
-
-
-
- ..\..\packages\xunit.abstractions.2.0.1\lib\netstandard1.0\xunit.abstractions.dll
- True
-
-
- ..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll
- True
-
-
- ..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.core.dll
- True
-
-
- ..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.execution.dotnet.dll
- True
-
-
- ..\..\packages\xunit.runner.devices.2.1.0\lib\Xamarin.iOS\xunit.runner.devices.dll
- True
-
-
- ..\..\packages\xunit.runner.utility.2.2.0-beta4-build3444\lib\netstandard1.1\xunit.runner.utility.dotnet.dll
- True
-
-
-
-
-
-
-
-
- {f7b6a162-bc4d-4924-b16a-713f9b0344e7}
- eShopOnContainers.UnitTests
-
-
-
-
-
-
- Este proyecto hace referencia a los paquetes NuGet que faltan en este equipo. Use la restauración de paquetes NuGet para descargarlos. Para obtener más información, consulte http://go.microsoft.com/fwlink/?LinkID=322105. El archivo que falta es {0}.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Services/Basket/Basket.API/Basket.API.csproj b/src/Services/Basket/Basket.API/Basket.API.csproj
index 36fc95496..087fdca66 100644
--- a/src/Services/Basket/Basket.API/Basket.API.csproj
+++ b/src/Services/Basket/Basket.API/Basket.API.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp2.2
$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
..\..\..\..\docker-compose.dcproj
@@ -13,24 +13,34 @@
+
+
+
+
+
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
diff --git a/src/Services/Basket/Basket.API/BasketSettings.cs b/src/Services/Basket/Basket.API/BasketSettings.cs
index 9d143545a..064d615ec 100644
--- a/src/Services/Basket/Basket.API/BasketSettings.cs
+++ b/src/Services/Basket/Basket.API/BasketSettings.cs
@@ -3,7 +3,5 @@
public class BasketSettings
{
public string ConnectionString { get; set; }
-
- public string EventBusConnection { get; set; }
}
}
diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs
index c7fdac57d..7bab4e969 100644
--- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs
+++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs
@@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.eShopOnContainers.Services.Basket.API.Services;
+using Microsoft.Extensions.Logging;
+using Serilog.Context;
using System;
using System.Net;
using System.Threading.Tasks;
@@ -13,53 +15,49 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
- public class BasketController : Controller
+ [ApiController]
+ public class BasketController : ControllerBase
{
private readonly IBasketRepository _repository;
- private readonly IIdentityService _identitySvc;
+ private readonly IIdentityService _identityService;
private readonly IEventBus _eventBus;
+ private readonly ILogger _logger;
- public BasketController(IBasketRepository repository,
+ public BasketController(
+ ILogger logger,
+ IBasketRepository repository,
IIdentityService identityService,
IEventBus eventBus)
{
+ _logger = logger;
_repository = repository;
- _identitySvc = identityService;
+ _identityService = identityService;
_eventBus = eventBus;
}
- // GET /id
[HttpGet("{id}")]
[ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)]
- public async Task Get(string id)
+ public async Task> GetBasketByIdAsync(string id)
{
var basket = await _repository.GetBasketAsync(id);
- if (basket == null)
- {
- return Ok(new CustomerBasket(id) { });
- }
- return Ok(basket);
+ return basket ?? new CustomerBasket(id);
}
- // POST /value
[HttpPost]
[ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)]
- public async Task Post([FromBody]CustomerBasket value)
+ public async Task> UpdateBasketAsync([FromBody]CustomerBasket value)
{
- var basket = await _repository.UpdateBasketAsync(value);
-
- return Ok(basket);
+ return await _repository.UpdateBasketAsync(value);
}
[Route("checkout")]
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.Accepted)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
- public async Task Checkout([FromBody]BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId)
+ public async Task CheckoutAsync([FromBody]BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId)
{
- var userId = _identitySvc.GetUserIdentity();
-
+ var userId = _identityService.GetUserIdentity();
basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ?
guid : basketCheckout.RequestId;
@@ -80,17 +78,28 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
// Once basket is checkout, sends an integration event to
// ordering.api to convert basket to order and proceeds with
// order creation process
- _eventBus.Publish(eventMessage);
+ try
+ {
+ _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", eventMessage.Id, Program.AppName, eventMessage);
+
+ _eventBus.Publish(eventMessage);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName);
+
+ throw;
+ }
return Accepted();
}
// DELETE api/values/5
[HttpDelete("{id}")]
- public void Delete(string id)
+ [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)]
+ public async Task DeleteBasketByIdAsync(string id)
{
- _repository.DeleteBasketAsync(id);
+ await _repository.DeleteBasketAsync(id);
}
-
}
}
diff --git a/src/Services/Basket/Basket.API/Dockerfile b/src/Services/Basket/Basket.API/Dockerfile
index 7027cfc2d..a5ccd56f5 100644
--- a/src/Services/Basket/Basket.API/Dockerfile
+++ b/src/Services/Basket/Basket.API/Dockerfile
@@ -1,14 +1,20 @@
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
-FROM microsoft/dotnet:2.1-sdk AS build
+FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/Services/Basket/Basket.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app
+FROM build as functionaltest
+WORKDIR /src/src/Services/Basket/Basket.FunctionalTests
+
+FROM build as unittest
+WORKDIR /src/src/Services/Basket/Basket.UnitTests
+
FROM build AS publish
RUN dotnet publish --no-restore -c Release -o /app
diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs
index 7caa9740d..f1c7b08d0 100644
--- a/src/Services/Basket/Basket.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs
+++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs
@@ -19,11 +19,13 @@ namespace Basket.API.Infrastructure.Filters
operation.Responses.Add("401", new Response { Description = "Unauthorized" });
operation.Responses.Add("403", new Response { Description = "Forbidden" });
- operation.Security = new List>>();
- operation.Security.Add(new Dictionary>
+ operation.Security = new List>>
{
- { "oauth2", new [] { "basketapi" } }
- });
+ new Dictionary>
+ {
+ { "oauth2", new [] { "basketapi" } }
+ }
+ };
}
}
}
diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs
index 19ae1b594..cb7b6a2d6 100644
--- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs
+++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs
@@ -1,6 +1,9 @@
using Basket.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
+using Microsoft.eShopOnContainers.Services.Basket.API;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
+using Microsoft.Extensions.Logging;
+using Serilog.Context;
using System;
using System.Threading.Tasks;
@@ -9,15 +12,24 @@ namespace Basket.API.IntegrationEvents.EventHandling
public class OrderStartedIntegrationEventHandler : IIntegrationEventHandler
{
private readonly IBasketRepository _repository;
+ private readonly ILogger _logger;
- public OrderStartedIntegrationEventHandler(IBasketRepository repository)
+ public OrderStartedIntegrationEventHandler(
+ IBasketRepository repository,
+ ILogger logger)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderStartedIntegrationEvent @event)
{
- await _repository.DeleteBasketAsync(@event.UserId.ToString());
+ using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
+ {
+ _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
+
+ await _repository.DeleteBasketAsync(@event.UserId.ToString());
+ }
}
}
}
diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs
index 546483b40..c27200e6f 100644
--- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs
+++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs
@@ -1,6 +1,8 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
+using Microsoft.Extensions.Logging;
+using Serilog.Context;
using System;
using System.Linq;
using System.Threading.Tasks;
@@ -9,22 +11,31 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even
{
public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler
{
+ private readonly ILogger _logger;
private readonly IBasketRepository _repository;
- public ProductPriceChangedIntegrationEventHandler(IBasketRepository repository)
+ public ProductPriceChangedIntegrationEventHandler(
+ ILogger logger,
+ IBasketRepository repository)
{
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
public async Task Handle(ProductPriceChangedIntegrationEvent @event)
{
- var userIds = _repository.GetUsers();
-
- foreach (var id in userIds)
+ using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
- var basket = await _repository.GetBasketAsync(id);
+ _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
- await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket);
+ var userIds = _repository.GetUsers();
+
+ foreach (var id in userIds)
+ {
+ var basket = await _repository.GetBasketAsync(id);
+
+ await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket);
+ }
}
}
@@ -35,17 +46,19 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even
if (itemsToUpdate != null)
{
+ _logger.LogInformation("----- ProductPriceChangedIntegrationEventHandler - Updating items in basket for user: {BuyerId} ({@Items})", basket.BuyerId, itemsToUpdate);
+
foreach (var item in itemsToUpdate)
{
- if(item.UnitPrice == oldPrice)
- {
+ if (item.UnitPrice == oldPrice)
+ {
var originalPrice = item.UnitPrice;
item.UnitPrice = newPrice;
item.OldUnitPrice = originalPrice;
}
}
await _repository.UpdateBasketAsync(basket);
- }
+ }
}
}
}
diff --git a/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs b/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs
index f748d2c25..5000e6c29 100644
--- a/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs
+++ b/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs
@@ -10,7 +10,6 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
public class RedisBasketRepository : IBasketRepository
{
private readonly ILogger _logger;
-
private readonly ConnectionMultiplexer _redis;
private readonly IDatabase _database;
@@ -30,12 +29,14 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
{
var server = GetServer();
var data = server.Keys();
+
return data?.Select(k => k.ToString());
}
public async Task GetBasketAsync(string customerId)
{
var data = await _database.StringGetAsync(customerId);
+
if (data.IsNullOrEmpty)
{
return null;
@@ -47,6 +48,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model
public async Task UpdateBasketAsync(CustomerBasket basket)
{
var created = await _database.StringSetAsync(basket.BuyerId, JsonConvert.SerializeObject(basket));
+
if (!created)
{
_logger.LogInformation("Problem occur persisting the item.");
diff --git a/src/Services/Basket/Basket.API/Program.cs b/src/Services/Basket/Basket.API/Program.cs
index 303a4625d..893a52400 100644
--- a/src/Services/Basket/Basket.API/Program.cs
+++ b/src/Services/Basket/Basket.API/Program.cs
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using Serilog;
using System;
using System.IO;
@@ -11,45 +12,80 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
{
public class Program
{
- public static void Main(string[] args)
+ public static readonly string Namespace = typeof(Program).Namespace;
+ public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
+
+ public static int Main(string[] args)
{
- BuildWebHost(args).Run();
+ var configuration = GetConfiguration();
+
+ Log.Logger = CreateSerilogLogger(configuration);
+
+ try
+ {
+ Log.Information("Configuring web host ({ApplicationContext})...", AppName);
+ var host = BuildWebHost(configuration, args);
+
+ Log.Information("Starting web host ({ApplicationContext})...", AppName);
+ host.Run();
+
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
+ return 1;
+ }
+ finally
+ {
+ Log.CloseAndFlush();
+ }
}
- public static IWebHost BuildWebHost(string[] args) =>
+ private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
+ .CaptureStartupErrors(false)
.UseFailing(options =>
- {
- options.ConfigPath = "/Failing";
- })
- .UseHealthChecks("/hc")
- .UseContentRoot(Directory.GetCurrentDirectory())
+ options.ConfigPath = "/Failing")
.UseStartup()
- .ConfigureAppConfiguration((builderContext, config) =>
- {
- var builtConfig = config.Build();
-
- var configurationBuilder = new ConfigurationBuilder();
-
- if (Convert.ToBoolean(builtConfig["UseVault"]))
- {
- configurationBuilder.AddAzureKeyVault(
- $"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
- builtConfig["Vault:ClientId"],
- builtConfig["Vault:ClientSecret"]);
- }
-
- configurationBuilder.AddEnvironmentVariables();
-
- config.AddConfiguration(configurationBuilder.Build());
- })
- .ConfigureLogging((hostingContext, builder) =>
- {
- builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- builder.AddConsole();
- builder.AddDebug();
- })
.UseApplicationInsights()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .UseConfiguration(configuration)
+ .UseSerilog()
.Build();
+
+ private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
+ {
+ var seqServerUrl = configuration["Serilog:SeqServerUrl"];
+
+ return new LoggerConfiguration()
+ .MinimumLevel.Verbose()
+ .Enrich.WithProperty("ApplicationContext", AppName)
+ .Enrich.FromLogContext()
+ .WriteTo.Console()
+ .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
+ .ReadFrom.Configuration(configuration)
+ .CreateLogger();
+ }
+
+ private static IConfiguration GetConfiguration()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddEnvironmentVariables();
+
+ var config = builder.Build();
+
+ if (config.GetValue("UseVault", false))
+ {
+ builder.AddAzureKeyVault(
+ $"https://{config["Vault:Name"]}.vault.azure.net/",
+ config["Vault:ClientId"],
+ config["Vault:ClientSecret"]);
+ }
+
+ return builder.Build();
+ }
}
}
diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs
index 7d65ba8b4..235b787d0 100644
--- a/src/Services/Basket/Basket.API/Startup.cs
+++ b/src/Services/Basket/Basket.API/Startup.cs
@@ -4,12 +4,16 @@ 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;
using Microsoft.Azure.ServiceBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
@@ -21,7 +25,7 @@ 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.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
@@ -30,7 +34,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
{
@@ -47,26 +50,23 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
- RegisterAppInsights(services);
+ RegisterAppInsights(services);
// Add framework services.
services.AddMvc(options =>
- {
- options.Filters.Add(typeof(HttpGlobalExceptionFilter));
- options.Filters.Add(typeof(ValidateModelStateFilter));
+ {
+ options.Filters.Add(typeof(HttpGlobalExceptionFilter));
+ options.Filters.Add(typeof(ValidateModelStateFilter));
- }).AddControllersAsServices();
+ })
+ .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
+ .AddControllersAsServices();
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);
+ services.Configure(Configuration);
//By connecting here we are making sure that our service
//cannot start until redis is ready. This might slow down startup,
@@ -159,7 +159,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
- builder => builder.AllowAnyOrigin()
+ builder => builder
+ .SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
@@ -180,8 +181,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
- loggerFactory.AddAzureWebAppDiagnostics();
- loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
+ //loggerFactory.AddAzureWebAppDiagnostics();
+ //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
@@ -189,10 +190,16 @@ 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));
-#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+ app.UseHealthChecks("/liveness", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Contains("self")
+ });
app.UseStaticFiles();
app.UseCors("CorsPolicy");
@@ -221,7 +228,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
- services.EnableKubernetes();
+ services.AddApplicationInsightsKubernetesEnricher();
}
if (orchestratorType?.ToUpper() == "SF")
{
@@ -309,6 +316,42 @@ 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.AddCheck("self", () => HealthCheckResult.Healthy());
+
+ 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/Basket/Basket.API/appsettings.json b/src/Services/Basket/Basket.API/appsettings.json
index 4bff4d70d..33f1c299f 100644
--- a/src/Services/Basket/Basket.API/appsettings.json
+++ b/src/Services/Basket/Basket.API/appsettings.json
@@ -1,10 +1,13 @@
{
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
+ "Serilog": {
+ "SeqServerUrl": null,
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft": "Warning",
+ "Microsoft.eShopOnContainers": "Information",
+ "System": "Warning"
+ }
}
},
"IdentityUrl": "http://localhost:5105",
diff --git a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj
index 21c64158c..e781b218d 100644
--- a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj
+++ b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp2.2
false
@@ -17,12 +17,15 @@
-
-
-
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
-
+
diff --git a/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs
index 5c7ecdef5..83d507ade 100644
--- a/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs
+++ b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs
@@ -5,7 +5,7 @@ using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
-using WebMVC.Models;
+using WebMVC.Services.ModelDTOs;
using Xunit;
namespace Basket.FunctionalTests
diff --git a/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs b/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs
index 123967558..84d41aa1a 100644
--- a/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs
+++ b/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs
@@ -1,5 +1,7 @@
-using Microsoft.eShopOnContainers.Services.Basket.API;
+using Basket.FunctionalTests.Base;
+using Microsoft.eShopOnContainers.Services.Basket.API;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
@@ -13,54 +15,60 @@ using Xunit;
namespace Basket.FunctionalTests
{
public class RedisBasketRepositoryTests
+ : BasketScenarioBase
{
- private Mock> _optionsMock;
-
- public RedisBasketRepositoryTests()
- {
- _optionsMock = new Mock>();
- }
[Fact]
public async Task UpdateBasket_return_and_add_basket()
{
- var redisBasketRepository = BuildBasketRepository();
-
- var basket = await redisBasketRepository.UpdateBasketAsync(new CustomerBasket("customerId")
+ using (var server = CreateServer())
{
- BuyerId = "buyerId",
- Items = BuildBasketItems()
- });
+ var redis = server.Host.Services.GetRequiredService();
- Assert.NotNull(basket);
- Assert.Single(basket.Items);
+ var redisBasketRepository = BuildBasketRepository(redis);
+
+ var basket = await redisBasketRepository.UpdateBasketAsync(new CustomerBasket("customerId")
+ {
+ BuyerId = "buyerId",
+ Items = BuildBasketItems()
+ });
+
+ Assert.NotNull(basket);
+ Assert.Single(basket.Items);
+ }
+
+
}
[Fact]
public async Task Delete_Basket_return_null()
{
- var redisBasketRepository = BuildBasketRepository();
- var basket = await redisBasketRepository.UpdateBasketAsync(new CustomerBasket("customerId")
+ using (var server = CreateServer())
{
- BuyerId = "buyerId",
- Items = BuildBasketItems()
- });
+ var redis = server.Host.Services.GetRequiredService();
- var deleteResult = await redisBasketRepository.DeleteBasketAsync("buyerId");
+ var redisBasketRepository = BuildBasketRepository(redis);
- var result = await redisBasketRepository.GetBasketAsync(basket.BuyerId);
+ var basket = await redisBasketRepository.UpdateBasketAsync(new CustomerBasket("customerId")
+ {
+ BuyerId = "buyerId",
+ Items = BuildBasketItems()
+ });
- Assert.True(deleteResult);
- Assert.Null(result);
+ var deleteResult = await redisBasketRepository.DeleteBasketAsync("buyerId");
+
+ var result = await redisBasketRepository.GetBasketAsync(basket.BuyerId);
+
+ Assert.True(deleteResult);
+ Assert.Null(result);
+ }
}
- RedisBasketRepository BuildBasketRepository()
+ RedisBasketRepository BuildBasketRepository(ConnectionMultiplexer connMux)
{
var loggerFactory = new LoggerFactory();
- var configuration = ConfigurationOptions.Parse("127.0.0.1", true);
- configuration.ResolveDns = true;
- return new RedisBasketRepository(loggerFactory, ConnectionMultiplexer.Connect(configuration));
+ return new RedisBasketRepository(loggerFactory, connMux);
}
List BuildBasketItems()
diff --git a/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs
index 88dda058d..0045ce4aa 100644
--- a/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs
+++ b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.Controllers;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
+using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.Collections.Generic;
@@ -20,12 +21,14 @@ namespace UnitTest.Basket.Application
private readonly Mock _basketRepositoryMock;
private readonly Mock _identityServiceMock;
private readonly Mock _serviceBusMock;
+ private readonly Mock> _loggerMock;
public BasketWebApiTest()
{
_basketRepositoryMock = new Mock();
_identityServiceMock = new Mock();
_serviceBusMock = new Mock();
+ _loggerMock = new Mock>();
}
[Fact]
@@ -43,11 +46,15 @@ namespace UnitTest.Basket.Application
//Act
var basketController = new BasketController(
- _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
- var actionResult = await basketController.Get(fakeCustomerId) as OkObjectResult;
+ _loggerMock.Object,
+ _basketRepositoryMock.Object,
+ _identityServiceMock.Object,
+ _serviceBusMock.Object);
+
+ var actionResult = await basketController.GetBasketByIdAsync(fakeCustomerId);
//Assert
- Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK);
+ Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK);
Assert.Equal(((CustomerBasket)actionResult.Value).BuyerId, fakeCustomerId);
}
@@ -65,14 +72,17 @@ namespace UnitTest.Basket.Application
//Act
var basketController = new BasketController(
- _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
+ _loggerMock.Object,
+ _basketRepositoryMock.Object,
+ _identityServiceMock.Object,
+ _serviceBusMock.Object);
- var actionResult = await basketController.Post(fakeCustomerBasket) as OkObjectResult;
+ var actionResult = await basketController.UpdateBasketAsync(fakeCustomerBasket);
//Assert
- Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK);
+ Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK);
Assert.Equal(((CustomerBasket)actionResult.Value).BuyerId, fakeCustomerId);
- }
+ }
[Fact]
public async Task Doing_Checkout_Without_Basket_Should_Return_Bad_Request()
@@ -84,9 +94,12 @@ namespace UnitTest.Basket.Application
//Act
var basketController = new BasketController(
- _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
+ _loggerMock.Object,
+ _basketRepositoryMock.Object,
+ _identityServiceMock.Object,
+ _serviceBusMock.Object);
- var result = await basketController.Checkout(new BasketCheckout(), Guid.NewGuid().ToString()) as BadRequestResult;
+ var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as BadRequestResult;
Assert.NotNull(result);
}
@@ -102,7 +115,10 @@ namespace UnitTest.Basket.Application
_identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId);
var basketController = new BasketController(
- _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
+ _loggerMock.Object,
+ _basketRepositoryMock.Object,
+ _identityServiceMock.Object,
+ _serviceBusMock.Object);
basketController.ControllerContext = new ControllerContext()
{
@@ -114,7 +130,7 @@ namespace UnitTest.Basket.Application
};
//Act
- var result = await basketController.Checkout(new BasketCheckout(), Guid.NewGuid().ToString()) as AcceptedResult;
+ var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as AcceptedResult;
_serviceBusMock.Verify(mock => mock.Publish(It.IsAny()), Times.Once);
@@ -122,7 +138,7 @@ namespace UnitTest.Basket.Application
}
private CustomerBasket GetCustomerBasketFake(string fakeCustomerId)
- {
+ {
return new CustomerBasket(fakeCustomerId)
{
Items = new List()
diff --git a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj
index cc2326841..bf5cb593a 100644
--- a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj
+++ b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj
@@ -1,18 +1,21 @@
- netcoreapp2.1
+ netcoreapp2.2
false
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
-
-
+
+
diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj
index 90abe593a..983cc291a 100644
--- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj
+++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp2.2
portable
true
Catalog.API
@@ -34,15 +34,26 @@
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -50,10 +61,6 @@
-
-
-
-
diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
index 584f7a0af..d9fa4002e 100644
--- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
+++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
@@ -28,7 +28,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
_catalogIntegrationEventService = catalogIntegrationEventService ?? throw new ArgumentNullException(nameof(catalogIntegrationEventService));
_settings = settings.Value;
- ((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
+ context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
// GET api/v1/[controller]/items[?pageSize=3&pageIndex=10]
@@ -36,11 +36,19 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
[Route("items")]
[ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)]
- public async Task Items([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0, [FromQuery] string ids = null)
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
+ public async Task ItemsAsync([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0, string ids = null)
{
if (!string.IsNullOrEmpty(ids))
{
- return GetItemsByIds(ids);
+ var items = await GetItemsByIdsAsync(ids);
+
+ if (!items.Any())
+ {
+ return BadRequest("ids value invalid. Must be comma-separated list of numbers");
+ }
+
+ return Ok(items);
}
var totalItems = await _catalogContext.CatalogItems
@@ -54,37 +62,36 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
itemsOnPage = ChangeUriPlaceholder(itemsOnPage);
- var model = new PaginatedItemsViewModel(
- pageIndex, pageSize, totalItems, itemsOnPage);
+ var model = new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage);
return Ok(model);
}
- private IActionResult GetItemsByIds(string ids)
+ private async Task> GetItemsByIdsAsync(string ids)
{
- var numIds = ids.Split(',')
- .Select(id => (Ok: int.TryParse(id, out int x), Value: x));
+ var numIds = ids.Split(',').Select(id => (Ok: int.TryParse(id, out int x), Value: x));
if (!numIds.All(nid => nid.Ok))
{
- return BadRequest("ids value invalid. Must be comma-separated list of numbers");
+ return new List();
}
var idsToSelect = numIds
.Select(id => id.Value);
- var items = _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToList();
+ var items = await _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToListAsync();
items = ChangeUriPlaceholder(items);
- return Ok(items);
+ return items;
}
[HttpGet]
[Route("items/{id:int}")]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
+ [ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(CatalogItem), (int)HttpStatusCode.OK)]
- public async Task GetItemById(int id)
+ public async Task> ItemByIdAsync(int id)
{
if (id <= 0)
{
@@ -95,11 +102,12 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
var baseUri = _settings.PicBaseUrl;
var azureStorageEnabled = _settings.AzureStorageEnabled;
+
item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled);
if (item != null)
{
- return Ok(item);
+ return item;
}
return NotFound();
@@ -107,11 +115,10 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
// GET api/v1/[controller]/items/withname/samplename[?pageSize=3&pageIndex=10]
[HttpGet]
- [Route("[action]/withname/{name:minlength(1)}")]
+ [Route("items/withname/{name:minlength(1)}")]
[ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)]
- public async Task Items(string name, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
+ public async Task>> ItemsWithNameAsync(string name, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
{
-
var totalItems = await _catalogContext.CatalogItems
.Where(c => c.Name.StartsWith(name))
.LongCountAsync();
@@ -124,17 +131,14 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
itemsOnPage = ChangeUriPlaceholder(itemsOnPage);
- var model = new PaginatedItemsViewModel(
- pageIndex, pageSize, totalItems, itemsOnPage);
-
- return Ok(model);
+ return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage);
}
// GET api/v1/[controller]/items/type/1/brand[?pageSize=3&pageIndex=10]
[HttpGet]
- [Route("[action]/type/{catalogTypeId}/brand/{catalogBrandId:int?}")]
+ [Route("items/type/{catalogTypeId}/brand/{catalogBrandId:int?}")]
[ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)]
- public async Task Items(int catalogTypeId, int? catalogBrandId, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
+ public async Task>> ItemsByTypeIdAndBrandIdAsync(int catalogTypeId, int? catalogBrandId, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
{
var root = (IQueryable)_catalogContext.CatalogItems;
@@ -155,16 +159,14 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
itemsOnPage = ChangeUriPlaceholder(itemsOnPage);
- var model = new PaginatedItemsViewModel(
- pageIndex, pageSize, totalItems, itemsOnPage);
-
- return Ok(model);
+ return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage);
}
+
// GET api/v1/[controller]/items/type/all/brand[?pageSize=3&pageIndex=10]
[HttpGet]
- [Route("[action]/type/all/brand/{catalogBrandId:int?}")]
+ [Route("items/type/all/brand/{catalogBrandId:int?}")]
[ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)]
- public async Task Items(int? catalogBrandId, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
+ public async Task>> ItemsByBrandIdAsync(int? catalogBrandId, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
{
var root = (IQueryable)_catalogContext.CatalogItems;
@@ -183,34 +185,25 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
itemsOnPage = ChangeUriPlaceholder(itemsOnPage);
- var model = new PaginatedItemsViewModel(
- pageIndex, pageSize, totalItems, itemsOnPage);
-
- return Ok(model);
+ return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage);
}
// GET api/v1/[controller]/CatalogTypes
[HttpGet]
- [Route("[action]")]
- [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
- public async Task CatalogTypes()
+ [Route("catalogtypes")]
+ [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
+ public async Task>> CatalogTypesAsync()
{
- var items = await _catalogContext.CatalogTypes
- .ToListAsync();
-
- return Ok(items);
+ return await _catalogContext.CatalogTypes.ToListAsync();
}
// GET api/v1/[controller]/CatalogBrands
[HttpGet]
- [Route("[action]")]
- [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
- public async Task CatalogBrands()
+ [Route("catalogbrands")]
+ [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)]
+ public async Task>> CatalogBrandsAsync()
{
- var items = await _catalogContext.CatalogBrands
- .ToListAsync();
-
- return Ok(items);
+ return await _catalogContext.CatalogBrands.ToListAsync();
}
//PUT api/v1/[controller]/items
@@ -218,10 +211,9 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType((int)HttpStatusCode.Created)]
- public async Task UpdateProduct([FromBody]CatalogItem productToUpdate)
+ public async Task UpdateProductAsync([FromBody]CatalogItem productToUpdate)
{
- var catalogItem = await _catalogContext.CatalogItems
- .SingleOrDefaultAsync(i => i.Id == productToUpdate.Id);
+ var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id);
if (catalogItem == null)
{
@@ -231,7 +223,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
var oldPrice = catalogItem.Price;
var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price;
-
// Update current product
catalogItem = productToUpdate;
_catalogContext.CatalogItems.Update(catalogItem);
@@ -252,14 +243,14 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
await _catalogContext.SaveChangesAsync();
}
- return CreatedAtAction(nameof(GetItemById), new { id = productToUpdate.Id }, null);
+ return CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null);
}
//POST api/v1/[controller]/items
[Route("items")]
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.Created)]
- public async Task CreateProduct([FromBody]CatalogItem product)
+ public async Task CreateProductAsync([FromBody]CatalogItem product)
{
var item = new CatalogItem
{
@@ -270,18 +261,20 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
PictureFileName = product.PictureFileName,
Price = product.Price
};
+
_catalogContext.CatalogItems.Add(item);
await _catalogContext.SaveChangesAsync();
- return CreatedAtAction(nameof(GetItemById), new { id = item.Id }, null);
+ return CreatedAtAction(nameof(ItemByIdAsync), new { id = item.Id }, null);
}
//DELETE api/v1/[controller]/id
[Route("{id}")]
[HttpDelete]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
- public async Task DeleteProduct(int id)
+ [ProducesResponseType((int)HttpStatusCode.NotFound)]
+ public async Task DeleteProductAsync(int id)
{
var product = _catalogContext.CatalogItems.SingleOrDefault(x => x.Id == id);
diff --git a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs
index c7af86eed..7d798597e 100644
--- a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs
+++ b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs
@@ -9,8 +9,9 @@ using System.Threading.Tasks;
// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
-{
- public class PicController : Controller
+{
+ [ApiController]
+ public class PicController : ControllerBase
{
private readonly IHostingEnvironment _env;
private readonly CatalogContext _catalogContext;
@@ -27,7 +28,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
// GET: //
- public async Task GetImage(int catalogItemId)
+ public async Task GetImageAsync(int catalogItemId)
{
if (catalogItemId <= 0)
{
diff --git a/src/Services/Catalog/Catalog.API/Dockerfile b/src/Services/Catalog/Catalog.API/Dockerfile
index 4cbc9c8aa..a9fefb765 100644
--- a/src/Services/Catalog/Catalog.API/Dockerfile
+++ b/src/Services/Catalog/Catalog.API/Dockerfile
@@ -1,14 +1,20 @@
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
-FROM microsoft/dotnet:2.1-sdk AS build
+FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/Services/Catalog/Catalog.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app
+FROM build as unittest
+WORKDIR /src/src/Services/Catalog/Catalog.UnitTests
+
+FROM build as functionaltest
+WORKDIR /src/src/Services/Catalog/Catalog.FunctionalTests
+
FROM build AS publish
RUN dotnet publish --no-restore -c Release -o /app
diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs
index e7ea1e09d..8bdd2a401 100644
--- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs
+++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs
@@ -76,14 +76,14 @@
}
catch (Exception ex)
{
- logger.LogError(ex.Message);
+ logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPreconfiguredCatalogBrands();
}
return File.ReadAllLines(csvFileCatalogBrands)
.Skip(1) // skip header row
.SelectTry(x => CreateCatalogBrand(x))
- .OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
+ .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@@ -131,14 +131,14 @@
}
catch (Exception ex)
{
- logger.LogError(ex.Message);
+ logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPreconfiguredCatalogTypes();
}
return File.ReadAllLines(csvFileCatalogTypes)
.Skip(1) // skip header row
.SelectTry(x => CreateCatalogType(x))
- .OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
+ .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@@ -186,7 +186,7 @@
}
catch (Exception ex)
{
- logger.LogError(ex.Message);
+ logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPreconfiguredItems();
}
@@ -197,7 +197,7 @@
.Skip(1) // skip header row
.Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") )
.SelectTry(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup))
- .OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
+ .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@@ -377,7 +377,7 @@
sleepDurationProvider: retry => TimeSpan.FromSeconds(5),
onRetry: (exception, timeSpan, retry, ctx) =>
{
- logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}");
+ logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries);
}
);
}
diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
index 4c4c746a6..1c1dfd45f 100644
--- a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
+++ b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
@@ -49,7 +49,7 @@ namespace Catalog.API.Infrastructure.Filters
if (env.IsDevelopment())
{
- json.DeveloperMeesage = context.Exception;
+ json.DeveloperMessage = context.Exception;
}
context.Result = new InternalServerErrorObjectResult(json);
@@ -62,7 +62,7 @@ namespace Catalog.API.Infrastructure.Filters
{
public string[] Messages { get; set; }
- public object DeveloperMeesage { get; set; }
+ public object DeveloperMessage { get; set; }
}
}
}
diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs
index 1b82251e3..3b9476b9f 100644
--- a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs
+++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs
@@ -4,7 +4,10 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
+using Microsoft.eShopOnContainers.Services.Catalog.API;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
+using Microsoft.Extensions.Logging;
+using Serilog.Context;
using System;
using System.Data.Common;
using System.Threading.Tasks;
@@ -17,10 +20,15 @@ namespace Catalog.API.IntegrationEvents
private readonly IEventBus _eventBus;
private readonly CatalogContext _catalogContext;
private readonly IIntegrationEventLogService _eventLogService;
+ private readonly ILogger _logger;
- public CatalogIntegrationEventService(IEventBus eventBus, CatalogContext catalogContext,
- Func integrationEventLogServiceFactory)
+ public CatalogIntegrationEventService(
+ ILogger logger,
+ IEventBus eventBus,
+ CatalogContext catalogContext,
+ Func integrationEventLogServiceFactory)
{
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext));
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
@@ -29,21 +37,33 @@ namespace Catalog.API.IntegrationEvents
public async Task PublishThroughEventBusAsync(IntegrationEvent evt)
{
- _eventBus.Publish(evt);
+ try
+ {
+ _logger.LogInformation("----- Publishing integration event: {IntegrationEventId_published} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt);
- await _eventLogService.MarkEventAsPublishedAsync(evt);
+ await _eventLogService.MarkEventAsInProgressAsync(evt.Id);
+ _eventBus.Publish(evt);
+ await _eventLogService.MarkEventAsPublishedAsync(evt.Id);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt);
+ await _eventLogService.MarkEventAsFailedAsync(evt.Id);
+ }
}
public async Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt)
{
+ _logger.LogInformation("----- CatalogIntegrationEventService - Saving changes and integrationEvent: {IntegrationEventId}", evt.Id);
+
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
- await ResilientTransaction.New(_catalogContext)
- .ExecuteAsync(async () => {
- // Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction
- await _catalogContext.SaveChangesAsync();
- await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction());
- });
+ await ResilientTransaction.New(_catalogContext).ExecuteAsync(async () =>
+ {
+ // Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction
+ await _catalogContext.SaveChangesAsync();
+ await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction());
+ });
}
}
}
diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs
index 0f30a3e0a..493a271cc 100644
--- a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs
+++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs
@@ -8,39 +8,51 @@
using System.Linq;
using global::Catalog.API.IntegrationEvents;
using IntegrationEvents.Events;
+ using Serilog.Context;
+ using Microsoft.Extensions.Logging;
public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler :
IIntegrationEventHandler
{
private readonly CatalogContext _catalogContext;
private readonly ICatalogIntegrationEventService _catalogIntegrationEventService;
+ private readonly ILogger _logger;
- public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(CatalogContext catalogContext,
- ICatalogIntegrationEventService catalogIntegrationEventService)
+ public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(
+ CatalogContext catalogContext,
+ ICatalogIntegrationEventService catalogIntegrationEventService,
+ ILogger logger)
{
_catalogContext = catalogContext;
_catalogIntegrationEventService = catalogIntegrationEventService;
+ _logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
- public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent command)
+ public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event)
{
- var confirmedOrderStockItems = new List();
-
- foreach (var orderStockItem in command.OrderStockItems)
+ using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
- var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
- var hasStock = catalogItem.AvailableStock >= orderStockItem.Units;
- var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);
+ _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
+
+ var confirmedOrderStockItems = new List();
+
+ foreach (var orderStockItem in @event.OrderStockItems)
+ {
+ var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
+ var hasStock = catalogItem.AvailableStock >= orderStockItem.Units;
+ var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);
+
+ confirmedOrderStockItems.Add(confirmedOrderStockItem);
+ }
+
+ var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
+ ? (IntegrationEvent)new OrderStockRejectedIntegrationEvent(@event.OrderId, confirmedOrderStockItems)
+ : new OrderStockConfirmedIntegrationEvent(@event.OrderId);
+
+ await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
+ await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
- confirmedOrderStockItems.Add(confirmedOrderStockItem);
}
-
- var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
- ? (IntegrationEvent) new OrderStockRejectedIntegrationEvent(command.OrderId, confirmedOrderStockItems)
- : new OrderStockConfirmedIntegrationEvent(command.OrderId);
-
- await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
- await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
}
}
}
\ No newline at end of file
diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs
index 0a45547f9..7d383254f 100644
--- a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs
+++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs
@@ -4,28 +4,40 @@
using System.Threading.Tasks;
using Infrastructure;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events;
+ using Microsoft.Extensions.Logging;
+ using Serilog.Context;
public class OrderStatusChangedToPaidIntegrationEventHandler :
IIntegrationEventHandler
{
private readonly CatalogContext _catalogContext;
+ private readonly ILogger _logger;
- public OrderStatusChangedToPaidIntegrationEventHandler(CatalogContext catalogContext)
+ public OrderStatusChangedToPaidIntegrationEventHandler(
+ CatalogContext catalogContext,
+ ILogger logger)
{
_catalogContext = catalogContext;
+ _logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
- public async Task Handle(OrderStatusChangedToPaidIntegrationEvent command)
+ public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event)
{
- //we're not blocking stock/inventory
- foreach (var orderStockItem in command.OrderStockItems)
+ using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
- var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
+ _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
+
+ //we're not blocking stock/inventory
+ foreach (var orderStockItem in @event.OrderStockItems)
+ {
+ var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
+
+ catalogItem.RemoveStock(orderStockItem.Units);
+ }
+
+ await _catalogContext.SaveChangesAsync();
- catalogItem.RemoveStock(orderStockItem.Units);
}
-
- await _catalogContext.SaveChangesAsync();
}
}
}
\ No newline at end of file
diff --git a/src/Services/Catalog/Catalog.API/Program.cs b/src/Services/Catalog/Catalog.API/Program.cs
index 8f3910c84..c75b56c39 100644
--- a/src/Services/Catalog/Catalog.API/Program.cs
+++ b/src/Services/Catalog/Catalog.API/Program.cs
@@ -6,61 +6,100 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Serilog;
using System;
using System.IO;
+
namespace Microsoft.eShopOnContainers.Services.Catalog.API
{
public class Program
{
- public static void Main(string[] args)
+ public static readonly string Namespace = typeof(Program).Namespace;
+ public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
+
+ public static int Main(string[] args)
{
- BuildWebHost(args)
- .MigrateDbContext((context,services)=>
+ var configuration = GetConfiguration();
+
+ Log.Logger = CreateSerilogLogger(configuration);
+
+ try
+ {
+ Log.Information("Configuring web host ({ApplicationContext})...", AppName);
+ var host = BuildWebHost(configuration, args);
+
+ Log.Information("Applying migrations ({ApplicationContext})...", AppName);
+ host.MigrateDbContext((context, services) =>
{
var env = services.GetService();
var settings = services.GetService>();
var logger = services.GetService>();
new CatalogContextSeed()
- .SeedAsync(context,env,settings,logger)
- .Wait();
-
+ .SeedAsync(context, env, settings, logger)
+ .Wait();
})
- .MigrateDbContext((_,__)=> { })
- .Run();
+ .MigrateDbContext((_, __) => { });
+
+ Log.Information("Starting web host ({ApplicationContext})...", AppName);
+ host.Run();
+
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
+ return 1;
+ }
+ finally
+ {
+ Log.CloseAndFlush();
+ }
}
- public static IWebHost BuildWebHost(string[] args) =>
+ private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
- .UseStartup()
+ .CaptureStartupErrors(false)
+ .UseStartup()
.UseApplicationInsights()
- .UseHealthChecks("/hc")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseWebRoot("Pics")
- .ConfigureAppConfiguration((builderContext, config) =>
- {
- var builtConfig = config.Build();
+ .UseConfiguration(configuration)
+ .UseSerilog()
+ .Build();
- var configurationBuilder = new ConfigurationBuilder();
+ private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
+ {
+ var seqServerUrl = configuration["Serilog:SeqServerUrl"];
- if (Convert.ToBoolean(builtConfig["UseVault"]))
- {
- configurationBuilder.AddAzureKeyVault(
- $"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
- builtConfig["Vault:ClientId"],
- builtConfig["Vault:ClientSecret"]);
- }
+ return new LoggerConfiguration()
+ .MinimumLevel.Verbose()
+ .Enrich.WithProperty("ApplicationContext", AppName)
+ .Enrich.FromLogContext()
+ .WriteTo.Console()
+ .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
+ .ReadFrom.Configuration(configuration)
+ .CreateLogger();
+ }
- configurationBuilder.AddEnvironmentVariables();
+ private static IConfiguration GetConfiguration()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddEnvironmentVariables();
- config.AddConfiguration(configurationBuilder.Build());
- })
- .ConfigureLogging((hostingContext, builder) =>
- {
- builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- builder.AddConsole();
- builder.AddDebug();
- })
- .Build();
+ var config = builder.Build();
+
+ if (config.GetValue("UseVault", false))
+ {
+ builder.AddAzureKeyVault(
+ $"https://{config["Vault:Name"]}.vault.azure.net/",
+ config["Vault:ClientId"],
+ config["Vault:ClientSecret"]);
+ }
+
+ return builder.Build();
+ }
}
}
\ No newline at end of file
diff --git a/src/Services/Catalog/Catalog.API/Properties/launchSettings.json b/src/Services/Catalog/Catalog.API/Properties/launchSettings.json
index 2b21ca280..8f2cde4db 100644
--- a/src/Services/Catalog/Catalog.API/Properties/launchSettings.json
+++ b/src/Services/Catalog/Catalog.API/Properties/launchSettings.json
@@ -13,7 +13,10 @@
"launchBrowser": true,
"launchUrl": "/swagger",
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
+ "ConnectionString": "server=localhost,5433;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word",
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "EventBusConnection": "localhost",
+ "Serilog:SeqServerUrl": "http://locahost:5340"
}
},
"Microsoft.eShopOnContainers.Services.Catalog.API": {
diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs
index 408f870af..0258a0a98 100644
--- a/src/Services/Catalog/Catalog.API/Startup.cs
+++ b/src/Services/Catalog/Catalog.API/Startup.cs
@@ -22,13 +22,15 @@ 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;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.eShopOnContainers.Services.Catalog.API
{
@@ -49,7 +51,8 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
.AddCustomOptions(Configuration)
.AddIntegrationServices(Configuration)
.AddEventBus(Configuration)
- .AddSwagger();
+ .AddSwagger()
+ .AddCustomHealthCheck(Configuration);
var container = new ContainerBuilder();
container.Populate(services);
@@ -61,20 +64,27 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
{
//Configure logs
- loggerFactory.AddAzureWebAppDiagnostics();
- loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
+ //loggerFactory.AddAzureWebAppDiagnostics();
+ //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
- loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
+ loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
-#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
+ app.UseHealthChecks("/hc", new HealthCheckOptions()
+ {
+ Predicate = _ => true,
+ ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
+ });
+
+ app.UseHealthChecks("/liveness", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Contains("self")
+ });
app.UseCors("CorsPolicy");
@@ -107,7 +117,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
- services.EnableKubernetes();
+ services.AddApplicationInsightsKubernetesEnricher();
}
if (orchestratorType?.ToUpper() == "SF")
{
@@ -120,33 +130,19 @@ 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));
- }).AddControllersAsServices();
+ {
+ options.Filters.Add(typeof(HttpGlobalExceptionFilter));
+ })
+ .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
+ .AddControllersAsServices();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
- builder => builder.AllowAnyOrigin()
+ builder => builder
+ .SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
@@ -155,6 +151,50 @@ 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
+ .AddCheck("self", () => HealthCheckResult.Healthy())
+ .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 =>
@@ -232,7 +272,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddTransient>(
- sp => (DbConnection c) => new IntegrationEventLogService(c));
+ sp => (DbConnection c) => new IntegrationEventLogService(c));
services.AddTransient();
diff --git a/src/Services/Catalog/Catalog.API/appsettings.json b/src/Services/Catalog/Catalog.API/appsettings.json
index 0bf489699..b26f63bff 100644
--- a/src/Services/Catalog/Catalog.API/appsettings.json
+++ b/src/Services/Catalog/Catalog.API/appsettings.json
@@ -2,12 +2,15 @@
"ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word",
"PicBaseUrl": "http://localhost:5101/api/v1/catalog/items/[0]/pic/",
"UseCustomizationData": false,
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Trace",
- "System": "Information",
- "Microsoft": "Information"
+ "Serilog": {
+ "SeqServerUrl": null,
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft": "Warning",
+ "Microsoft.eShopOnContainers": "Information",
+ "System": "Warning"
+ }
}
},
"AzureServiceBusEnabled": false,
diff --git a/src/Services/Catalog/Catalog.API/web.config b/src/Services/Catalog/Catalog.API/web.config
index e04a0397b..2157aef31 100644
--- a/src/Services/Catalog/Catalog.API/web.config
+++ b/src/Services/Catalog/Catalog.API/web.config
@@ -2,8 +2,10 @@
-
+
-
+
+
+
\ No newline at end of file
diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj
index a8e691e22..07e9e2991 100644
--- a/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj
+++ b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp2.2
false
@@ -33,10 +33,13 @@
-
-
-
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj
index a336ba98d..6a9a2bf99 100644
--- a/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj
+++ b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj
@@ -1,17 +1,20 @@
- netcoreapp2.1
+ netcoreapp2.2
false
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
-
+
diff --git a/src/Services/Identity/Identity.API/.bowerrc b/src/Services/Identity/Identity.API/.bowerrc
deleted file mode 100644
index 6406626ab..000000000
--- a/src/Services/Identity/Identity.API/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory": "wwwroot/lib"
-}
diff --git a/src/Services/Identity/Identity.API/Configuration/Config.cs b/src/Services/Identity/Identity.API/Configuration/Config.cs
index b780d420f..dcb3a8c7a 100644
--- a/src/Services/Identity/Identity.API/Configuration/Config.cs
+++ b/src/Services/Identity/Identity.API/Configuration/Config.cs
@@ -17,7 +17,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
new ApiResource("locations", "Locations Service"),
new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"),
new ApiResource("webshoppingagg", "Web Shopping Aggregator"),
- new ApiResource("orders.signalrhub", "Ordering Signalr Hub")
+ new ApiResource("orders.signalrhub", "Ordering Signalr Hub"),
+ new ApiResource("webhooks", "Webhooks registration Service"),
};
}
@@ -33,7 +34,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
}
// client want to access resources (aka scopes)
- public static IEnumerable GetClients(Dictionary clientsUrl)
+ public static IEnumerable GetClients(Dictionary clientsUrl)
{
return new List
{
@@ -57,8 +58,9 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"locations",
"marketing",
"webshoppingagg",
- "orders.signalrhub"
- }
+ "orders.signalrhub",
+ "webhooks"
+ },
},
new Client
{
@@ -74,7 +76,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
RequireConsent = false,
RequirePkce = true,
PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
- AllowedCorsOrigins = { "http://eshopxamarin" },
+ //AllowedCorsOrigins = { "http://eshopxamarin" },
AllowedScopes = new List
{
IdentityServerConstants.StandardScopes.OpenId,
@@ -84,7 +86,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"basket",
"locations",
"marketing",
- "mobileshoppingagg"
+ "mobileshoppingagg",
+ "webhooks"
},
//Allow requesting refresh tokens for long lived API access
AllowOfflineAccess = true,
@@ -122,8 +125,43 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"locations",
"marketing",
"webshoppingagg",
- "orders.signalrhub"
+ "orders.signalrhub",
+ "webhooks"
},
+ AccessTokenLifetime = 60*60*2, // 2 hours
+ IdentityTokenLifetime= 60*60*2 // 2 hours
+ },
+ new Client
+ {
+ ClientId = "webhooksclient",
+ ClientName = "Webhooks Client",
+ ClientSecrets = new List
+ {
+ new Secret("secret".Sha256())
+ },
+ ClientUri = $"{clientsUrl["WebhooksWeb"]}", // public uri of the client
+ AllowedGrantTypes = GrantTypes.Hybrid,
+ AllowAccessTokensViaBrowser = false,
+ RequireConsent = false,
+ AllowOfflineAccess = true,
+ AlwaysIncludeUserClaimsInIdToken = true,
+ RedirectUris = new List
+ {
+ $"{clientsUrl["WebhooksWeb"]}/signin-oidc"
+ },
+ PostLogoutRedirectUris = new List
+ {
+ $"{clientsUrl["WebhooksWeb"]}/signout-callback-oidc"
+ },
+ AllowedScopes = new List
+ {
+ IdentityServerConstants.StandardScopes.OpenId,
+ IdentityServerConstants.StandardScopes.Profile,
+ IdentityServerConstants.StandardScopes.OfflineAccess,
+ "webhooks"
+ },
+ AccessTokenLifetime = 60*60*2, // 2 hours
+ IdentityTokenLifetime= 60*60*2 // 2 hours
},
new Client
{
@@ -155,7 +193,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"basket",
"locations",
"marketing",
- "webshoppingagg"
+ "webshoppingagg",
+ "webhooks"
},
},
new Client
@@ -247,8 +286,22 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
{
"webshoppingagg"
}
- }
+ },
+ new Client
+ {
+ ClientId = "webhooksswaggerui",
+ ClientName = "WebHooks Service Swagger UI",
+ AllowedGrantTypes = GrantTypes.Implicit,
+ AllowAccessTokensViaBrowser = true,
+ RedirectUris = { $"{clientsUrl["WebhooksApi"]}/swagger/oauth2-redirect.html" },
+ PostLogoutRedirectUris = { $"{clientsUrl["WebhooksApi"]}/swagger/" },
+
+ AllowedScopes =
+ {
+ "webhooks"
+ }
+ }
};
}
}
diff --git a/src/Services/Identity/Identity.API/Controllers/AccountController.cs b/src/Services/Identity/Identity.API/Controllers/AccountController.cs
index 79e9c247e..6e9bbce16 100644
--- a/src/Services/Identity/Identity.API/Controllers/AccountController.cs
+++ b/src/Services/Identity/Identity.API/Controllers/AccountController.cs
@@ -1,4 +1,9 @@
-using IdentityModel;
+using System;
+using System.Linq;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using IdentityModel;
using IdentityServer4;
using IdentityServer4.Models;
using IdentityServer4.Services;
@@ -11,16 +16,11 @@ using Microsoft.eShopOnContainers.Services.Identity.API.Models;
using Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels;
using Microsoft.eShopOnContainers.Services.Identity.API.Services;
using Microsoft.Extensions.Logging;
-using System;
-using System.Linq;
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
{
///
- /// This sample controller implements a typical login/logout/provision workflow for local and external accounts.
+ /// This sample controller implements a typical login/logout/provision workflow for local accounts.
/// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production!
/// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval
///
@@ -58,8 +58,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context?.IdP != null)
{
- // if IdP is passed, then bypass showing the login screen
- return ExternalLogin(context.IdP, returnUrl);
+ throw new NotImplementedException("External login is not implemented!");
}
var vm = await BuildLoginViewModelAsync(returnUrl, context);
@@ -79,9 +78,16 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
if (ModelState.IsValid)
{
var user = await _loginService.FindByUsername(model.Email);
+
if (await _loginService.ValidateCredentials(user, model.Password))
{
- AuthenticationProperties props = null;
+ var props = new AuthenticationProperties
+ {
+ ExpiresUtc = DateTimeOffset.UtcNow.AddHours(2),
+ AllowRefresh = true,
+ RedirectUri = model.ReturnUrl
+ };
+
if (model.RememberMe)
{
props = new AuthenticationProperties
@@ -91,8 +97,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
};
};
- await _loginService.SignIn(user);
-
+ await _loginService.SignInAsync(user, props);
+
// make sure the returnUrl is still valid, and if yes - redirect back to authorize endpoint
if (_interaction.IsValidReturnUrl(model.ReturnUrl))
{
@@ -113,7 +119,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
return View(vm);
}
- async Task BuildLoginViewModelAsync(string returnUrl, AuthorizationRequest context)
+ private async Task BuildLoginViewModelAsync(string returnUrl, AuthorizationRequest context)
{
var allowLocal = true;
if (context?.ClientId != null)
@@ -132,7 +138,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
};
}
- async Task BuildLoginViewModelAsync(LoginViewModel model)
+ private async Task BuildLoginViewModelAsync(LoginViewModel model)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
var vm = await BuildLoginViewModelAsync(model.ReturnUrl, context);
@@ -193,7 +199,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
try
{
-
+
// hack: try/catch to handle social providers that throw
await HttpContext.SignOutAsync(idp, new AuthenticationProperties
{
@@ -202,13 +208,15 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
}
catch (Exception ex)
{
- _logger.LogCritical(ex.Message);
+ _logger.LogError(ex, "LOGOUT ERROR: {ExceptionMessage}", ex.Message);
}
}
// delete authentication cookie
await HttpContext.SignOutAsync();
+ await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
+
// set this so UI rendering sees an anonymous user
HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity());
@@ -229,28 +237,6 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
return Redirect(redirectUrl);
}
- ///
- /// initiate roundtrip to external authentication provider
- ///
- [HttpGet]
- public IActionResult ExternalLogin(string provider, string returnUrl)
- {
- if (returnUrl != null)
- {
- returnUrl = UrlEncoder.Default.Encode(returnUrl);
- }
- returnUrl = "/account/externallogincallback?returnUrl=" + returnUrl;
-
- // start challenge and roundtrip the return URL
- var props = new AuthenticationProperties
- {
- RedirectUri = returnUrl,
- Items = { { "scheme", provider } }
- };
- return new ChallengeResult(provider, props);
- }
-
-
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
diff --git a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs
index 3dbf28171..a1fda6455 100644
--- a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs
+++ b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs
@@ -51,7 +51,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
{
retryForAvaiability++;
- logger.LogError(ex.Message,$"There is an error migrating data for ApplicationDbContext");
+ logger.LogError(ex, "EXCEPTION ERROR while migrating {DbContextName}", nameof(ApplicationDbContext));
await SeedAsync(context,env,logger,settings, retryForAvaiability);
}
@@ -80,7 +80,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
}
catch (Exception ex)
{
- logger.LogError(ex.Message);
+ logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetDefaultUser();
}
@@ -89,7 +89,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
.Skip(1) // skip header column
.Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") )
.SelectTry(column => CreateApplicationUser(column, csvheaders))
- .OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
+ .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null)
.ToList();
@@ -199,7 +199,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
if (!File.Exists(imagesZipFile))
{
- logger.LogError($" zip file '{imagesZipFile}' does not exists.");
+ logger.LogError("Zip file '{ZipFileName}' does not exists.", imagesZipFile);
return;
}
@@ -221,14 +221,14 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
}
else
{
- logger.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'");
+ logger.LogWarning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile);
}
}
}
}
catch (Exception ex)
{
- logger.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}");
+ logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); ;
}
}
}
diff --git a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs
index 3547c053a..653c6fded 100644
--- a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs
+++ b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs
@@ -28,6 +28,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
clientUrls.Add("OrderingApi", configuration.GetValue("OrderingApiClient"));
clientUrls.Add("MobileShoppingAgg", configuration.GetValue("MobileShoppingAggClient"));
clientUrls.Add("WebShoppingAgg", configuration.GetValue("WebShoppingAggClient"));
+ clientUrls.Add("WebhooksApi", configuration.GetValue("WebhooksApiClient"));
+ clientUrls.Add("WebhooksWeb", configuration.GetValue("WebhooksWebClient"));
if (!context.Clients.Any())
{
diff --git a/src/Services/Identity/Identity.API/Dockerfile b/src/Services/Identity/Identity.API/Dockerfile
index 3931a135b..f3d18f113 100644
--- a/src/Services/Identity/Identity.API/Dockerfile
+++ b/src/Services/Identity/Identity.API/Dockerfile
@@ -1,20 +1,8 @@
-ARG NODE_IMAGE=node:8.11
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
+FROM microsoft/dotnet:2.2.0-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
-FROM microsoft/dotnet:2.1-sdk as dotnet-build
-WORKDIR /src
-
-FROM ${NODE_IMAGE} as node-build
-WORKDIR /web
-COPY src/Services/Identity/Identity.API .
-RUN npm install -g bower@1.8.4
-RUN bower install --allow-root
-
-FROM dotnet-build as build
-WORKDIR /src/src/Services/Identity/Identity.API/wwwroot
-COPY --from=node-build /web/wwwroot .
+FROM microsoft/dotnet:2.2.100-sdk AS build
WORKDIR /src
COPY . .
WORKDIR /src/src/Services/Identity/Identity.API
diff --git a/src/Services/Identity/Identity.API/Identity.API.csproj b/src/Services/Identity/Identity.API/Identity.API.csproj
index 96a45e5ef..fd5f1474e 100644
--- a/src/Services/Identity/Identity.API/Identity.API.csproj
+++ b/src/Services/Identity/Identity.API/Identity.API.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp2.2
aspnet-eShopOnContainers.Identity-90487118-103c-4ff0-b9da-e5e26f7ab0c5
..\..\..\..\docker-compose.dcproj
@@ -13,18 +13,28 @@
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -40,9 +50,6 @@
-
-
-
diff --git a/src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs b/src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs
deleted file mode 100644
index 5d4f597a2..000000000
--- a/src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels
-{
- public class ExternalLoginConfirmationViewModel
- {
- [Required]
- [EmailAddress]
- public string Email { get; set; }
- }
-}
diff --git a/src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs b/src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs
deleted file mode 100644
index 0b24c3b7c..000000000
--- a/src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Microsoft.AspNetCore.Http.Authentication;
-using Microsoft.AspNetCore.Identity;
-using System.Collections.Generic;
-
-namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ManageViewModels
-{
- public class ManageLoginsViewModel
- {
- public IList CurrentLogins { get; set; }
-
- public IList OtherLogins { get; set; }
- }
-}
diff --git a/src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs b/src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs
deleted file mode 100644
index c9171fcf3..000000000
--- a/src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ManageViewModels
-{
- public class RemoveLoginViewModel
- {
- public string LoginProvider { get; set; }
- public string ProviderKey { get; set; }
- }
-}
diff --git a/src/Services/Identity/Identity.API/Program.cs b/src/Services/Identity/Identity.API/Program.cs
index 6b8353062..8204a1ebf 100644
--- a/src/Services/Identity/Identity.API/Program.cs
+++ b/src/Services/Identity/Identity.API/Program.cs
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Serilog;
using System;
using System.IO;
@@ -13,63 +14,99 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
{
public class Program
{
- public static void Main(string[] args)
+ public static readonly string Namespace = typeof(Program).Namespace;
+ public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
+
+ public static int Main(string[] args)
{
- BuildWebHost(args)
- .MigrateDbContext((_, __) => { })
- .MigrateDbContext((context, services) =>
- {
- var env = services.GetService();
- var logger = services.GetService>();
- var settings = services.GetService>();
+ var configuration = GetConfiguration();
- new ApplicationDbContextSeed()
- .SeedAsync(context, env, logger, settings)
- .Wait();
- })
- .MigrateDbContext((context,services)=>
- {
- var configuration = services.GetService();
+ Log.Logger = CreateSerilogLogger(configuration);
- new ConfigurationDbContextSeed()
- .SeedAsync(context, configuration)
- .Wait();
- }).Run();
+ try
+ {
+ Log.Information("Configuring web host ({ApplicationContext})...", AppName);
+ var host = BuildWebHost(configuration, args);
+
+ Log.Information("Applying migrations ({ApplicationContext})...", AppName);
+ host.MigrateDbContext((_, __) => { })
+ .MigrateDbContext((context, services) =>
+ {
+ var env = services.GetService();
+ var logger = services.GetService>();
+ var settings = services.GetService>();
+
+ new ApplicationDbContextSeed()
+ .SeedAsync(context, env, logger, settings)
+ .Wait();
+ })
+ .MigrateDbContext((context, services) =>
+ {
+ new ConfigurationDbContextSeed()
+ .SeedAsync(context, configuration)
+ .Wait();
+ });
+
+ Log.Information("Starting web host ({ApplicationContext})...", AppName);
+ host.Run();
+
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
+ return 1;
+ }
+ finally
+ {
+ Log.CloseAndFlush();
+ }
}
- public static IWebHost BuildWebHost(string[] args) =>
+ private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
- .UseKestrel()
- .UseHealthChecks("/hc")
- .UseContentRoot(Directory.GetCurrentDirectory())
- .UseIISIntegration()
+ .CaptureStartupErrors(false)
.UseStartup()
- .ConfigureAppConfiguration((builderContext, config) =>
- {
- var builtConfig = config.Build();
-
- var configurationBuilder = new ConfigurationBuilder();
-
- if (Convert.ToBoolean(builtConfig["UseVault"]))
- {
- configurationBuilder.AddAzureKeyVault(
- $"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
- builtConfig["Vault:ClientId"],
- builtConfig["Vault:ClientSecret"]);
- }
-
- configurationBuilder.AddEnvironmentVariables();
-
- config.AddConfiguration(configurationBuilder.Build());
- })
- .ConfigureLogging((hostingContext, builder) =>
- {
- builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
- builder.AddConsole();
- builder.AddDebug();
- })
.UseApplicationInsights()
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .UseConfiguration(configuration)
+ .UseSerilog()
.Build();
+
+ private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
+ {
+ var seqServerUrl = configuration["Serilog:SeqServerUrl"];
+
+ return new LoggerConfiguration()
+ .MinimumLevel.Verbose()
+ .Enrich.WithProperty("ApplicationContext", AppName)
+ .Enrich.FromLogContext()
+ .WriteTo.Console()
+ .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
+ .ReadFrom.Configuration(configuration)
+ .CreateLogger();
+ }
+
+ private static IConfiguration GetConfiguration()
+ {
+ var builder = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddEnvironmentVariables();
+
+ var config = builder.Build();
+
+ if (config.GetValue("UseVault", false))
+ {
+ builder.AddAzureKeyVault(
+ $"https://{config["Vault:Name"]}.vault.azure.net/",
+ config["Vault:ClientId"],
+ config["Vault:ClientSecret"]);
+ }
+
+ return builder.Build();
+ }
+
}
}
diff --git a/src/Services/Identity/Identity.API/Services/EFLoginService.cs b/src/Services/Identity/Identity.API/Services/EFLoginService.cs
index 63c4d4b7e..f3a9d5a03 100644
--- a/src/Services/Identity/Identity.API/Services/EFLoginService.cs
+++ b/src/Services/Identity/Identity.API/Services/EFLoginService.cs
@@ -1,15 +1,17 @@
-using Microsoft.AspNetCore.Identity;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Identity;
using Microsoft.eShopOnContainers.Services.Identity.API.Models;
-using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Services
{
public class EFLoginService : ILoginService
{
- UserManager _userManager;
- SignInManager _signInManager;
+ private UserManager _userManager;
+ private SignInManager _signInManager;
- public EFLoginService(UserManager userManager, SignInManager signInManager) {
+ public EFLoginService(UserManager userManager, SignInManager signInManager)
+ {
_userManager = userManager;
_signInManager = signInManager;
}
@@ -24,8 +26,14 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Services
return await _userManager.CheckPasswordAsync(user, password);
}
- public Task SignIn(ApplicationUser user) {
+ public Task SignIn(ApplicationUser user)
+ {
return _signInManager.SignInAsync(user, true);
}
+
+ public Task SignInAsync(ApplicationUser user, AuthenticationProperties properties, string authenticationMethod = null)
+ {
+ return _signInManager.SignInAsync(user, properties, authenticationMethod);
+ }
}
}
diff --git a/src/Services/Identity/Identity.API/Services/ILoginService.cs b/src/Services/Identity/Identity.API/Services/ILoginService.cs
index 7bff7f272..8a977205b 100644
--- a/src/Services/Identity/Identity.API/Services/ILoginService.cs
+++ b/src/Services/Identity/Identity.API/Services/ILoginService.cs
@@ -1,11 +1,16 @@
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Services
{
public interface ILoginService
{
Task ValidateCredentials(T user, string password);
+
Task FindByUsername(string user);
+
Task SignIn(T user);
+
+ Task SignInAsync(T user, AuthenticationProperties properties, string authenticationMethod = null);
}
}
diff --git a/src/Services/Identity/Identity.API/Startup.cs b/src/Services/Identity/Identity.API/Startup.cs
index 255bb82b5..e672bab60 100644
--- a/src/Services/Identity/Identity.API/Startup.cs
+++ b/src/Services/Identity/Identity.API/Startup.cs
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Identity.API.Certificates;
using Microsoft.eShopOnContainers.Services.Identity.API.Data;
@@ -14,11 +15,13 @@ 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;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.eShopOnContainers.Services.Identity.API
{
@@ -39,12 +42,12 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
// Add framework services.
services.AddDbContext(options =>
options.UseSqlServer(Configuration["ConnectionString"],
- sqlServerOptionsAction: sqlOptions =>
- {
- sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
- //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
- sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
- }));
+ sqlServerOptionsAction: sqlOptions =>
+ {
+ sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
+ //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
+ sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
+ }));
services.AddIdentity()
.AddEntityFrameworkStores()
@@ -52,7 +55,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
services.Configure(Configuration);
- services.AddMvc();
+ services.AddMvc()
+ .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
if (Configuration.GetValue("IsClusterEnv") == bool.TrueString)
{
@@ -63,16 +67,12 @@ 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()
+ .AddCheck("self", () => HealthCheckResult.Healthy())
+ .AddSqlServer(Configuration["ConnectionString"],
+ name: "IdentityDB-check",
+ tags: new string[] { "IdentityDB" });
+
services.AddTransient, EFLoginService>();
services.AddTransient();
@@ -80,30 +80,34 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// Adds IdentityServer
- services.AddIdentityServer(x => x.IssuerUri = "null")
- .AddSigningCredential(Certificate.Get())
- .AddAspNetIdentity()
- .AddConfigurationStore(options =>
+ services.AddIdentityServer(x =>
+ {
+ x.IssuerUri = "null";
+ x.Authentication.CookieLifetime = TimeSpan.FromHours(2);
+ })
+ .AddSigningCredential(Certificate.Get())
+ .AddAspNetIdentity()
+ .AddConfigurationStore(options =>
+ {
+ options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
+ sqlServerOptionsAction: sqlOptions =>
+ {
+ sqlOptions.MigrationsAssembly(migrationsAssembly);
+ //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
+ sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
+ });
+ })
+ .AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
- sqlServerOptionsAction: sqlOptions =>
- {
- sqlOptions.MigrationsAssembly(migrationsAssembly);
- //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
- sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
- });
+ sqlServerOptionsAction: sqlOptions =>
+ {
+ sqlOptions.MigrationsAssembly(migrationsAssembly);
+ //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
+ sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
+ });
})
- .AddOperationalStore(options =>
- {
- options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
- sqlServerOptionsAction: sqlOptions =>
- {
- sqlOptions.MigrationsAssembly(migrationsAssembly);
- //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
- sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
- });
- })
- .Services.AddTransient();
+ .Services.AddTransient();
var container = new ContainerBuilder();
container.Populate(services);
@@ -114,10 +118,10 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
- loggerFactory.AddConsole(Configuration.GetSection("Logging"));
- loggerFactory.AddDebug();
- loggerFactory.AddAzureWebAppDiagnostics();
- loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
+ //loggerFactory.AddConsole(Configuration.GetSection("Logging"));
+ //loggerFactory.AddDebug();
+ //loggerFactory.AddAzureWebAppDiagnostics();
+ //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
if (env.IsDevelopment())
{
@@ -132,14 +136,20 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
- loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
+ loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
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
+ app.UseHealthChecks("/liveness", new HealthCheckOptions
+ {
+ Predicate = r => r.Name.Contains("self")
+ });
app.UseStaticFiles();
@@ -155,6 +165,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
// Adds IdentityServer
app.UseIdentityServer();
+ app.UseHttpsRedirection();
app.UseMvc(routes =>
{
routes.MapRoute(
@@ -171,7 +182,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
- services.EnableKubernetes();
+ services.AddApplicationInsightsKubernetesEnricher();
}
if (orchestratorType?.ToUpper() == "SF")
{
diff --git a/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml b/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml
index 60e420f64..4727229c7 100644
--- a/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml
+++ b/src/Services/Identity/Identity.API/Views/Shared/_Layout.cshtml
@@ -18,7 +18,7 @@
- | | |