diff --git a/.dockerignore b/.dockerignore index 5fbf98f1e..077c2c71e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,14 +5,11 @@ .vs .vscode docker-compose*.yml -docker-compose.dcproj -*.sln *.md hosts LICENSE *.testsettings vsts-docs -test ServiceFabric readme k8s diff --git a/README.md b/README.md index 839e2f186..c3c05d76b 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ However, this sample application should not be considered as an "eCommerce refer ![image](https://user-images.githubusercontent.com/1712635/40397331-059a7ec6-5de7-11e8-8542-a597eca16fef.png) -> Read the planned Roadmap and Milestones for future releases of eShopOnContainers within the Wiki for further info about possible new implementations and provide feedback at the ISSUES section if you'd like to see any specific scenario implemented or improved. Also, feel free to discuss on any current issue. +> Read the planned Roadmap within the Wiki for further info about possible new implementations and provide feedback at the ISSUES section if you'd like to see any specific scenario implemented or improved. Also, feel free to discuss on any current issue. ### Architecture overview This reference application is cross-platform at the server and client side, thanks to .NET Core services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS or Windows/UWP plus any browser for the client web apps. -The architecture proposes a microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the communication protocol between the client apps and the microservices and supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus (a light message broker, to choose between RabbitMQ or Azure Service Bus, underneath) plus other features defined at the roadmap. +The architecture proposes a microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the communication protocol between the client apps and the microservices and supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus (a light message broker, to choose between RabbitMQ or Azure Service Bus, underneath) plus other features defined at the roadmap.

@@ -83,8 +83,8 @@ The architecture proposes a microservice oriented architecture implementation wi > ### Important Note on EventBus > In this solution's current EventBus is a simplified implementation, mainly used for learning purposes (development and testing), so it doesn't handle all production scenarios, most notably on error handling.

> The following forks provide production environment level implementation examples with eShopOnContainers : -> * Implementation with [CAP](https://github.com/dotnetcore/CAP) : https://github.com/yang-xiaodong/eShopOnContainers > * Implementation with [NServiceBus](https://github.com/Particular/NServiceBus) : https://github.com/Particular/eShopOnContainers +> * Implementation with [CAP](https://github.com/dotnetcore/CAP) : https://github.com/yang-xiaodong/eShopOnContainers ## 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. @@ -129,25 +129,20 @@ Finally, those microservices are consumed by multiple client web and mobile apps ## Setting up your development environment for eShopOnContainers -### Visual Studio 2017 (or above) and Windows based -This is the more straightforward way to get started: -https://github.com/dotnet-architecture/eShopOnContainers/wiki/02.-Setting-eShopOnContainers-in-a-Visual-Studio-2017-environment -### CLI and Windows based -For those who prefer the CLI on Windows, using dotnet CLI, docker CLI and VS Code for Windows: -https://github.com/dotnet/eShopOnContainers/wiki/03.-Setting-the-eShopOnContainers-solution-up-in-a-Windows-CLI-environment-(dotnet-CLI,-Docker-CLI-and-VS-Code) +### Windows based (CLI and Visual Studio) -### CLI and Mac based -For those who prefer the CLI on a Mac, using dotnet CLI, docker CLI and VS Code for Mac: -https://github.com/dotnet-architecture/eShopOnContainers/wiki/04.-Setting-eShopOnContainer-solution-up-in-a-Mac,-VS-for-Mac-or-with-CLI-environment--(dotnet-CLI,-Docker-CLI-and-VS-Code) + + +### Mac based (CLI ans Visual Studio for Mac) + + ## Orchestrators: Kubernetes and Service Fabric + See at the [Wiki](https://github.com/dotnet-architecture/eShopOnContainers/wiki) the posts on setup/instructions about how to deploy to Kubernetes or Service Fabric in Azure (although you could also deploy to any other cloud or on-premises). ## Sending feedback and pull requests + As mentioned, we'd appreciate your feedback, improvements and ideas. You can create new issues at the issues section, do pull requests and/or send emails to **eshop_feedback@service.microsoft.com** - -## Questions -[QUESTION] Answer +1 if the solution is working for you (Through VS or CLI environment): -https://github.com/dotnet/eShopOnContainers/issues/107 diff --git a/build/azure-devops/buildimages.yaml b/build/azure-devops/buildimages.yaml index fc2a41e72..da40a7054 100644 --- a/build/azure-devops/buildimages.yaml +++ b/build/azure-devops/buildimages.yaml @@ -24,7 +24,7 @@ jobs: env: TAG: ${{ variables['Build.SourceBranchName'] }} PLATFORM: win - NODE_IMAGE: stefanscherer/node-windows:8.11 + NODE_IMAGE: stefanscherer/node-windows:10 - job: BuildLinux condition: ne('${{ variables['Build.Reason'] }}', 'PullRequest') pool: @@ -77,7 +77,7 @@ jobs: dockerComposeFileArgs: | TAG=${{ variables['Build.SourceBranchName'] }} PLATFORM=win - NODE_IMAGE=stefanscherer/node-windows:8.11 + NODE_IMAGE=stefanscherer/node-windows:10 - task: DockerCompose@0 displayName: Compose push ${{ parameters.services }} inputs: diff --git a/build/azure-devops/webmvc/azure-pipelines.yml b/build/azure-devops/webmvc/azure-pipelines.yml index fc9ba2856..fd637b260 100644 --- a/build/azure-devops/webmvc/azure-pipelines.yml +++ b/build/azure-devops/webmvc/azure-pipelines.yml @@ -15,6 +15,8 @@ jobs: parameters: services: webmvc registryEndpoint: $(registryEndpoint) + helmfrom: $(Build.SourcesDirectory)/k8s/helm + helmto: $(Build.ArtifactStagingDirectory)/k8s/helm - template: ../multiarch.yaml parameters: image: webmvc diff --git a/build/azure-devops/webspa/azure-pipelines.yml b/build/azure-devops/webspa/azure-pipelines.yml index c8fc28e8b..99d8d2daf 100644 --- a/build/azure-devops/webspa/azure-pipelines.yml +++ b/build/azure-devops/webspa/azure-pipelines.yml @@ -15,6 +15,8 @@ jobs: parameters: services: webspa registryEndpoint: $(registryEndpoint) + helmfrom: $(Build.SourcesDirectory)/k8s/helm + helmto: $(Build.ArtifactStagingDirectory)/k8s/helm - template: ../multiarch.yaml parameters: image: webspa diff --git a/build/azure-devops/webstatus/azure-pipelines.yml b/build/azure-devops/webstatus/azure-pipelines.yml index c12347c62..f361370e6 100644 --- a/build/azure-devops/webstatus/azure-pipelines.yml +++ b/build/azure-devops/webstatus/azure-pipelines.yml @@ -15,6 +15,8 @@ jobs: parameters: services: webstatus registryEndpoint: $(registryEndpoint) + helmfrom: $(Build.SourcesDirectory)/k8s/helm + helmto: $(Build.ArtifactStagingDirectory)/k8s/helm - template: ../multiarch.yaml parameters: image: webstatus diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 563d712c1..1ec7879b3 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -331,40 +331,40 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_URLS=http://0.0.0.0:80 - - HealthChecks-UI__HealthChecks__0__Name=WebMVC HTTP Check - - HealthChecks-UI__HealthChecks__0__Uri=http://webmvc/hc - - HealthChecks-UI__HealthChecks__1__Name=WebSPA HTTP Check - - HealthChecks-UI__HealthChecks__1__Uri=http://webspa/hc - - HealthChecks-UI__HealthChecks__2__Name=Web Shopping Aggregator GW HTTP Check - - HealthChecks-UI__HealthChecks__2__Uri=http://webshoppingagg/hc - - HealthChecks-UI__HealthChecks__3__Name=Mobile Shopping Aggregator HTTP Check - - HealthChecks-UI__HealthChecks__3__Uri=http://mobileshoppingagg/hc - - HealthChecks-UI__HealthChecks__4__Name=Mobile Shopping API GW HTTP Check - - HealthChecks-UI__HealthChecks__4__Uri=http://mobileshoppingapigw/hc - - HealthChecks-UI__HealthChecks__5__Name=Mobile Marketing API GW HTTP Check - - HealthChecks-UI__HealthChecks__5__Uri=http://mobilemarketingapigw/hc - - HealthChecks-UI__HealthChecks__6__Name=Web Shopping API GW HTTP Check - - HealthChecks-UI__HealthChecks__6__Uri=http://webshoppingapigw/hc - - HealthChecks-UI__HealthChecks__7__Name=Web Marketing API GW HTTP Check - - HealthChecks-UI__HealthChecks__7__Uri=http://webmarketingapigw/hc - - HealthChecks-UI__HealthChecks__8__Name=Ordering HTTP Check - - HealthChecks-UI__HealthChecks__8__Uri=http://ordering.api/hc - - HealthChecks-UI__HealthChecks__9__Name=Ordering HTTP Background Check - - HealthChecks-UI__HealthChecks__9__Uri=http://ordering.backgroundtasks/hc - - HealthChecks-UI__HealthChecks__10__Name=Basket HTTP Check - - HealthChecks-UI__HealthChecks__10__Uri=http://basket.api/hc - - HealthChecks-UI__HealthChecks__11__Name=Catalog HTTP Check - - HealthChecks-UI__HealthChecks__11__Uri=http://catalog.api/hc - - HealthChecks-UI__HealthChecks__12__Name=Identity HTTP Check - - HealthChecks-UI__HealthChecks__12__Uri=http://identity.api/hc - - HealthChecks-UI__HealthChecks__13__Name=Marketing HTTP Check - - HealthChecks-UI__HealthChecks__13__Uri=http://marketing.api/hc - - HealthChecks-UI__HealthChecks__14__Name=Locations HTTP Check - - HealthChecks-UI__HealthChecks__14__Uri=http://locations.api/hc - - HealthChecks-UI__HealthChecks__15__Name=Payments HTTP Check - - HealthChecks-UI__HealthChecks__15__Uri=http://payment.api/hc - - HealthChecks-UI__HealthChecks__16__Name=Ordering SignalRHub HTTP Check - - HealthChecks-UI__HealthChecks__16__Uri=http://ordering.signalrhub/hc + - HealthChecksUI__HealthChecks__0__Name=WebMVC HTTP Check + - HealthChecksUI__HealthChecks__0__Uri=http://webmvc/hc + - HealthChecksUI__HealthChecks__1__Name=WebSPA HTTP Check + - HealthChecksUI__HealthChecks__1__Uri=http://webspa/hc + - HealthChecksUI__HealthChecks__2__Name=Web Shopping Aggregator GW HTTP Check + - HealthChecksUI__HealthChecks__2__Uri=http://webshoppingagg/hc + - HealthChecksUI__HealthChecks__3__Name=Mobile Shopping Aggregator HTTP Check + - HealthChecksUI__HealthChecks__3__Uri=http://mobileshoppingagg/hc + - HealthChecksUI__HealthChecks__4__Name=Mobile Shopping API GW HTTP Check + - HealthChecksUI__HealthChecks__4__Uri=http://mobileshoppingapigw/hc + - HealthChecksUI__HealthChecks__5__Name=Mobile Marketing API GW HTTP Check + - HealthChecksUI__HealthChecks__5__Uri=http://mobilemarketingapigw/hc + - HealthChecksUI__HealthChecks__6__Name=Web Shopping API GW HTTP Check + - HealthChecksUI__HealthChecks__6__Uri=http://webshoppingapigw/hc + - HealthChecksUI__HealthChecks__7__Name=Web Marketing API GW HTTP Check + - HealthChecksUI__HealthChecks__7__Uri=http://webmarketingapigw/hc + - HealthChecksUI__HealthChecks__8__Name=Ordering HTTP Check + - HealthChecksUI__HealthChecks__8__Uri=http://ordering.api/hc + - HealthChecksUI__HealthChecks__9__Name=Ordering HTTP Background Check + - HealthChecksUI__HealthChecks__9__Uri=http://ordering.backgroundtasks/hc + - HealthChecksUI__HealthChecks__10__Name=Basket HTTP Check + - HealthChecksUI__HealthChecks__10__Uri=http://basket.api/hc + - HealthChecksUI__HealthChecks__11__Name=Catalog HTTP Check + - HealthChecksUI__HealthChecks__11__Uri=http://catalog.api/hc + - HealthChecksUI__HealthChecks__12__Name=Identity HTTP Check + - HealthChecksUI__HealthChecks__12__Uri=http://identity.api/hc + - HealthChecksUI__HealthChecks__13__Name=Marketing HTTP Check + - HealthChecksUI__HealthChecks__13__Uri=http://marketing.api/hc + - HealthChecksUI__HealthChecks__14__Name=Locations HTTP Check + - HealthChecksUI__HealthChecks__14__Uri=http://locations.api/hc + - HealthChecksUI__HealthChecks__15__Name=Payments HTTP Check + - HealthChecksUI__HealthChecks__15__Uri=http://payment.api/hc + - HealthChecksUI__HealthChecks__16__Name=Ordering SignalRHub HTTP Check + - HealthChecksUI__HealthChecks__16__Uri=http://ordering.signalrhub/hc - OrderingBackgroundTasksUrl=http://ordering.backgroundtasks/hc - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} - OrchestratorType=${ORCHESTRATOR_TYPE} diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml index afc7e4e91..b7499f061 100644 --- a/docker-compose.windows.yml +++ b/docker-compose.windows.yml @@ -20,17 +20,17 @@ services: identity.api: build: args: - NODE_IMAGE: stefanscherer/node-windows:8.11 + NODE_IMAGE: stefanscherer/node-windows:10 webspa: build: args: - NODE_IMAGE: stefanscherer/node-windows:8.11 + NODE_IMAGE: stefanscherer/node-windows:10 webmvc: build: args: - NODE_IMAGE: stefanscherer/node-windows:8.11 + NODE_IMAGE: stefanscherer/node-windows:10 networks: diff --git a/docker-compose.yml b/docker-compose.yml index 11839c8b8..6094f2ca2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: image: datalust/seq:latest sql.data: - image: microsoft/mssql-server-linux:2017-latest + image: mcr.microsoft.com/mssql/server:2017-latest nosql.data: image: mongo diff --git a/docs-kb/README.md b/docs-kb/README.md deleted file mode 100644 index ad5fbd79f..000000000 --- a/docs-kb/README.md +++ /dev/null @@ -1,6 +0,0 @@ -eShopOnContainers Knowledge Base -================================ - -This folder contains a set of posts created mostly from [issues on the repo](https://github.com/dotnet-architecture/eShopOnContainers/issues), in order to offer a brief introduction as well as links to deepen the knowledge on a given subject related to the application. - -[Simplified CQRS and DDD](simplified-cqrs-ddd/post.md) diff --git a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-00-24.png b/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-00-24.png deleted file mode 100644 index e1b073019..000000000 Binary files a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-00-24.png and /dev/null differ diff --git a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-13-35.png b/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-13-35.png deleted file mode 100644 index 7c6258a17..000000000 Binary files a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-13-35.png and /dev/null differ diff --git a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-22-23.png b/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-22-23.png deleted file mode 100644 index ad667c138..000000000 Binary files a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-22-23.png and /dev/null differ diff --git a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-40-25.png b/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-40-25.png deleted file mode 100644 index d32e4b6a8..000000000 Binary files a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-40-25.png and /dev/null differ diff --git a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-48-36.png b/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-48-36.png deleted file mode 100644 index db42f1e81..000000000 Binary files a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-48-36.png and /dev/null differ diff --git a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-52-58.png b/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-52-58.png deleted file mode 100644 index 5047148d5..000000000 Binary files a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_18-52-58.png and /dev/null differ diff --git a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_19-11-30.png b/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_19-11-30.png deleted file mode 100644 index ab42b4278..000000000 Binary files a/docs-kb/simplified-cqrs-ddd/devenv_2018-05-22_19-11-30.png and /dev/null differ diff --git a/docs-kb/simplified-cqrs-ddd/post.md b/docs-kb/simplified-cqrs-ddd/post.md deleted file mode 100644 index be7f9e176..000000000 --- a/docs-kb/simplified-cqrs-ddd/post.md +++ /dev/null @@ -1,93 +0,0 @@ -Simplified CQRS and DDD -======================= - -CQRS, for Command and Query Responsibility Segregation, is an architectural pattern that, in very simple terms, has two different ways to handle the application model. - -**Commands** are responsible for **changing** the application state, i.e. creating, updating and deleting entities (data). - -**Queries** are responsible for **reading** the application state, e.g. to display information to the user. - -**Commands** are made thinking about the Domain rules, restrictions and transaction boundaries. - -**Queries** are made thinking about the presentation layer, the client UI. - -When handling **commands**, the application model is usually represented by DDD constructs, e.g. Root aggregates, entities, value objects, etc., and there are usually some sort of rules that restrict the allowed state changes, e.g. An order has to be paid before dispatching. - -When handling **queries**, the application model is usually represented by entities and relations and can be read much like SQL queries to display information. - -Queries don't change state, so they can be run as much as required and will always return the same values (as long as the application state hasn't changed), i.e. queries are "idempotent". - -Why the separation? because the rules for **changing** the model can impose unnecessary constraints for **reading** the model, e.g. you might allow to change order items only before dispatching so the order is like the gate-keeper (root aggregate) to access the order items, but you might also want to view all orders for some catalog item, so you have to be able to access the order items first (in a read only way). - -In this simplified CQRS approach both the DDD model and the query model use the same database. - -**Commands** and **Queries** are located in the Application layer, because: - -1. It's where the composition of domain root aggregates occur (commands) and -2. It's close to the UI requirements and has access to the whole database of the microservice (queries). - -Ideally, root aggregates are ignorant of each other and it's the Application layer's responsibility to compose coordinated actions by means of domain events, because it knows about all root aggregates. - -Regarding **queries**, in a similar analysis, the Application layer knows about all entities and relationships in the database, beyond the restrictions of the root aggregates. - -Code ----- - -### CQRS -The CQRS pattern can be checked in the Ordering service: - -Commands and queries are clearly separated in the application layer (Ordering.API). - -**Solution Explorer [Ordering.API]:** -![](devenv_2018-05-22_18-00-24.png) - -Commands are basically read only Data Transfer Objects (DTO) that contain all data that's required to execute the operation. - -**CreateOrderCommand:** -![](devenv_2018-05-22_18-13-35.png) - -Each command has a specific command handler that's responsible for executing the operations intended for the command. - -**CreateOrderCommandHandler:** -![](devenv_2018-05-22_18-22-23.png) - -In this case: - -1. Creates an Order object (root aggregate) -2. Adds the order items using the root aggregate method -3. Adds the order through the repository -4. Saves the order - -Queries, on the other hand, just return whatever the UI needs, could be a domain object or collections of specific DTOs. - -**IOrderQueries:** -![](devenv_2018-05-22_18-40-25.png) - -And they are implemented as plain SQL queries, in this case using [Dapper](http://dapper-tutorial.net/ as the ORM. - -**OrderQueries:** -![](devenv_2018-05-22_18-48-36.png) - -There can even be specific ViewModels or DTOs just to get the query results. - -**OrderViewModel:** -![](devenv_2018-05-22_19-11-30.png) - -### DDD -The DDD pattern can be checked in the domain layer (Ordering.Domain) - -**Solution Explorer [Ordering.Domain + Ordering.Infrastructure]:** -![](devenv_2018-05-22_18-52-58.png) - -There you can see the Buyer aggregate and the Order aggregate, as well as the repository implementations in Ordering.Infrastructure. - -Command handlers from the application layer use the root aggregates from the Domain layer and the repository implementations from the Infrastructure layer, the latter through Dependency Injection. - -Further reading ---------------- - -* **Issue #592 - [Question] Ordering Queries**
https://github.com/dotnet-architecture/eShopOnContainers/issues/592 - -* **Applying simplified CQRS and DDD patterns in a microservice**
-https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/apply-simplified-microservice-cqrs-ddd-patterns - diff --git a/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.2.1.pdf b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.2.1.pdf new file mode 100644 index 000000000..b0552e400 Binary files /dev/null and b/docs/NET-Microservices-Architecture-for-Containerized-NET-Applications-(Microsoft-eBook)-v2.2.1.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 882775502..b0552e400 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/k8s/deploy-nodeports.ps1 b/k8s/deploy-nodeports.ps1 new file mode 100644 index 000000000..a10462b0b --- /dev/null +++ b/k8s/deploy-nodeports.ps1 @@ -0,0 +1,2 @@ +kubectl apply -f .\nodeports\rabbitmq-admin.yaml +kubectl apply -f .\nodeports\sql-services.yaml diff --git a/k8s/helm/deploy-all.ps1 b/k8s/helm/deploy-all.ps1 index 3cf6936bc..9b02ab5fe 100644 --- a/k8s/helm/deploy-all.ps1 +++ b/k8s/helm/deploy-all.ps1 @@ -10,7 +10,8 @@ Param( [parameter(Mandatory=$false)][string]$aksName="", [parameter(Mandatory=$false)][string]$aksRg="", [parameter(Mandatory=$false)][string]$imageTag="latest", - [parameter(Mandatory=$false)][bool]$useLocalk8s=$false + [parameter(Mandatory=$false)][bool]$useLocalk8s=$false, + [parameter(Mandatory=$false)][bool]$useLocalImages=$false ) $dns = $externalDns @@ -22,6 +23,12 @@ if ($useLocalk8s -eq $true) { $dns="localhost" } +$pullPolicy = "Always" + +if ($useLocalImages -eq $true) { + $pullPolicy = "IfNotPresent" +} + 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 @@ -82,7 +89,7 @@ if ($deployCharts) { } 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 $ingressValuesFile --set app.name=$appName --set inf.k8s.dns=$dns --set "ingress.hosts={$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 "ingress.hosts={$dns}" --set image.tag=$imageTag --set image.pullPolicy=$pullPolicy --name="$appName-$chart" $chart } } } diff --git a/k8s/helm/deploy-all.sh b/k8s/helm/deploy-all.sh index 705b172f5..6f6d868aa 100755 --- a/k8s/helm/deploy-all.sh +++ b/k8s/helm/deploy-all.sh @@ -14,30 +14,32 @@ Parameters: The resource group for the AKS cluster. Required when the registry (using the -r parameter) is set to "aks". -b | --build-solution Force a solution build before deployment (default: false). - -d | --dns - Specifies the external DNS/ IP address of the Kubernetes cluster. + -d | --dns | --dns aks + Specifies the external DNS/ IP address of the Kubernetes cluster. + If 'aks' is set as value, the DNS value is retrieved from the AKS. --aks-name and --aks-rg are needed. When --use-local-k8s is specified the external DNS is automatically set to localhost. -h | --help Displays this help text and exits the script. + --image-build + Build images (default is to not build all images). + --image-push + Upload images to the container registry (default is not pushing to the custom registry) -n | --app-name Specifies the name of the application (default: eshop). + --namespace + Specifies the namespace name to deploy the app. If it doesn't exists it will be created (default: eshop). -p | --docker-password The Docker password used to logon to the custom registry, supplied using the -r parameter. -r | --registry Specifies the container registry to use (required), e.g. myregistry.azurecr.io. --skip-clean Do not clean the Kubernetes cluster (default is to clean the cluster). - --skip-image-build - Do not build images (default is to build all images). - --skip-image-push - Do not upload images to the container registry (just run the Kubernetes deployment portion). - Default is to push the images to the container registry. --skip-infrastructure Do not deploy infrastructure resources (like sql-data, no-sql or redis). This is useful for production environments where infrastructure is hosted outside the Kubernetes cluster. -t | --tag - The tag used for the newly created docker images. Default: newly created, date-based timestamp, with 1-minute resolution. - -u | --docker-user + The tag used for the newly created docker images. Default: latest. + -u | --docker-username The Docker username used to logon to the custom registry, supplied using the -r parameter. --use-local-k8s Deploy to a locally installed Kubernetes (default: false). @@ -47,11 +49,10 @@ If using AKS and ACR see link for more info: https://docs.microsoft.com/en-us/azure/container-registry/container-registry-auth-aks WARNING! THE SCRIPT WILL COMPLETELY DESTROY ALL DEPLOYMENTS AND SERVICES VISIBLE -FROM THE CURRENT CONFIGURATION CONTEXT. -It is recommended that you create a separate namespace and confguration context -for the $app_name application, to isolate it from other applications on the cluster. +FROM THE CURRENT CONFIGURATION CONTEXT AND NAMESPACE. +It is recommended that you check your selected namespace, 'eshop' by default, is already in use. +Every deployment and service done in the namespace will be deleted. For more information see https://kubernetes.io/docs/tasks/administer-cluster/namespaces/ -You can use namespace.yaml file (in the same directory) to create the namespace. END } @@ -59,17 +60,18 @@ END app_name='eshop' aks_name='' aks_rg='' -build_images='yes' +build_images='' clean='yes' build_solution='' container_registry='' docker_password='' docker_username='' dns='' -image_tag=$(date '+%Y%m%d%H%M') -push_images='yes' +image_tag='latest' +push_images='' skip_infrastructure='' use_local_k8s='' +namespace='eshop' while [[ $# -gt 0 ]]; do case "$1" in @@ -86,15 +88,15 @@ while [[ $# -gt 0 ]]; do -n | --app-name ) app_name="$2"; shift 2;; -p | --docker-password ) - docker_password="$2"; shift;; + docker_password="$2"; shift 2;; -r | --registry ) container_registry="$2"; shift 2;; --skip-clean ) clean=''; shift ;; - --skip-image-build ) - build_images=''; shift ;; - --skip-image-push ) - push_images=''; shift ;; + --image-build ) + build_images='yes'; shift ;; + --image-push ) + push_images='yes'; shift ;; --skip-infrastructure ) skip_infrastructure='yes'; shift ;; -t | --tag ) @@ -103,6 +105,8 @@ while [[ $# -gt 0 ]]; do docker_username="$2"; shift 2;; --use-local-k8s ) use_local_k8s='yes'; shift ;; + --namespace ) + namespace="$2"; shift 2;; *) echo "Unknown option $1" usage; exit 2 ;; @@ -124,10 +128,26 @@ if [[ $build_images ]]; then docker rmi $(docker images -qf "dangling=true") fi +use_custom_registry='' + +if [[ -n $container_registry ]]; then + echo "################ Log into custom registry $container_registry ##################" + use_custom_registry='yes' + if [[ -z $docker_username ]] || [[ -z $docker_password ]]; then + echo "Error: Must use -u (--docker-username) AND -p (--docker-password) if specifying custom registry" + exit 1 + fi + docker login -u $docker_username -p $docker_password $container_registry +fi + if [[ $push_images ]]; then echo "#################### Pushing images to the container registry ####################" services=(basket.api catalog.api identity.api ordering.api marketing.api payment.api locations.api webmvc webspa webstatus) + if [[ -z "$(docker image ls -q --filter=reference=eshop/$service:$image_tag)" ]]; then + image_tag=linux-$image_tag + fi + for service in "${services[@]}" do echo "Pushing image for service $service..." @@ -152,14 +172,23 @@ if [[ $dns == "aks" ]]; then exit 1 fi - echo "Getting DNS of AKS of AKS $aks_name (in resource group $aks_rg)" - dns="$(az aks show -n $aks_name -g $aks_rg --query addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName)" - if [[ -z dns ]]; then + echo "Getting AKS cluster $aks_name AKS (in resource group $aks_rg)" + # JMESPath queries are case sensitive and httpapplicationrouting can be lowercase sometimes + jmespath_dnsqueries=(\ + addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName \ + addonProfiles.httpapplicationrouting.config.HTTPApplicationRoutingZoneName \ + ) + for q in "${jmespath_dnsqueries[@]}" + do + dns="$(az aks show -n $aks_name -g $aks_rg --query $q -o tsv)" + if [[ -n $dns ]]; then break; fi + done + if [[ -z $dns ]]; then echo "Error: when getting DNS of AKS $aks_name (in resource group $aks_rg). Please ensure AKS has httpRouting enabled AND Azure CLI is logged in and is of version 2.0.37 or higher." exit 1 fi - $dns=${dns//[\"]/""} echo "DNS base found is $dns. Will use $aks_name.$dns for the app!" + dns="$aks_name.$dns" fi # Initialization & check commands @@ -169,17 +198,12 @@ fi if [[ $clean ]]; then echo "Cleaning previous helm releases..." - helm delete --purge $(helm ls -q) - echo "Previous releases deleted" -fi - -use_custom_registry='' - -if [[ -n $container_registry ]]; then - use_custom_registry='yes' - if [[ -z $docker_user ]] || [[ -z $docker_password ]]; then - echo "Error: Must use -u (--docker-username) AND -p (--docker-password) if specifying custom registry" - exit 1 + if [[ -z $(helm ls -q --namespace $namespace) ]]; then + echo "No previous releases found" + else + helm delete --purge $(helm ls -q --namespace $namespace) + echo "Previous releases deleted" + waitsecs=10; while [ $waitsecs -gt 0 ]; do echo -ne "$waitsecs\033[0K\r"; sleep 1; : $((waitsecs--)); done fi fi @@ -191,7 +215,7 @@ if [[ !$skip_infrastructure ]]; then for infra in "${infras[@]}" do echo "Installing infrastructure: $infra" - helm install --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --name="$app_name-$infra" $infra + helm install --namespace $namespace --set "ingress.hosts={$dns}" --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --name="$app_name-$infra" $infra done fi @@ -199,9 +223,9 @@ for chart in "${charts[@]}" do echo "Installing: $chart" if [[ $use_custom_registry ]]; then - helm install --set inf.registry.server=$container_registry --set inf.registry.login=$docker_username --set inf.registry.pwd=$docker_password --set inf.registry.secretName=eshop-docker-scret --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always --name="$app_name-$chart" $chart + helm install --namespace $namespace --set "ingress.hosts={$dns}" --set inf.registry.server=$container_registry --set inf.registry.login=$docker_username --set inf.registry.pwd=$docker_password --set inf.registry.secretName=eshop-docker-scret --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always --name="$app_name-$chart" $chart elif [[ $chart != "eshop-common" ]]; then # eshop-common is ignored when no secret must be deployed - helm install --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always --name="$app_name-$chart" $chart + helm install --namespace $namespace --set "ingress.hosts={$dns}" --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always --name="$app_name-$chart" $chart fi done diff --git a/k8s/helm/webstatus/values.yaml b/k8s/helm/webstatus/values.yaml index 88cae5d7f..808fd8ba9 100644 --- a/k8s/helm/webstatus/values.yaml +++ b/k8s/helm/webstatus/values.yaml @@ -34,73 +34,73 @@ env: configmap: - name: ApplicationInsights__InstrumentationKey key: all__InstrumentationKey - - name: HealthChecks-UI__HealthChecks__0__Name + - name: HealthChecksUI__HealthChecks__0__Name key: name__mvc__hc - - name: HealthChecks-UI__HealthChecks__0__Uri + - name: HealthChecksUI__HealthChecks__0__Uri key: internalurls__mvc__hc - - name: HealthChecks-UI__HealthChecks__1__Name + - name: HealthChecksUI__HealthChecks__1__Name key: name__spa__hc - - name: HealthChecks-UI__HealthChecks__1__Uri + - name: HealthChecksUI__HealthChecks__1__Uri key: internalurls__spa__hc - - name: HealthChecks-UI__HealthChecks__2__Name + - name: HealthChecksUI__HealthChecks__2__Name key: name__apigwws__hc - - name: HealthChecks-UI__HealthChecks__2__Uri + - name: HealthChecksUI__HealthChecks__2__Uri key: internalurls__apigwws__hc - - name: HealthChecks-UI__HealthChecks__3__Name + - name: HealthChecksUI__HealthChecks__3__Name key: name__apigwwm__hc - - name: HealthChecks-UI__HealthChecks__3__Uri + - name: HealthChecksUI__HealthChecks__3__Uri key: internalurls__apigwwm__hc - - name: HealthChecks-UI__HealthChecks__4__Name + - name: HealthChecksUI__HealthChecks__4__Name key: name__apigwms__hc - - name: HealthChecks-UI__HealthChecks__4__Uri + - name: HealthChecksUI__HealthChecks__4__Uri key: internalurls__apigwms__hc - - name: HealthChecks-UI__HealthChecks__5__Name + - name: HealthChecksUI__HealthChecks__5__Name key: name__apigwmm__hc - - name: HealthChecks-UI__HealthChecks__5__Uri + - name: HealthChecksUI__HealthChecks__5__Uri key: internalurls__apigwmm__hc - - name: HealthChecks-UI__HealthChecks__6__Name + - name: HealthChecksUI__HealthChecks__6__Name key: name__apigwwsagg__hc - - name: HealthChecks-UI__HealthChecks__6__Uri + - name: HealthChecksUI__HealthChecks__6__Uri key: internalurls__apigwwsagg__hc - - name: HealthChecks-UI__HealthChecks__7__Name + - name: HealthChecksUI__HealthChecks__7__Name key: name__apigwmsagg__hc - - name: HealthChecks-UI__HealthChecks__7__Uri + - name: HealthChecksUI__HealthChecks__7__Uri key: internalurls__apigwmsagg__hc - - name: HealthChecks-UI__HealthChecks__8__Name + - name: HealthChecksUI__HealthChecks__8__Name key: name__ordering__hc - - name: HealthChecks-UI__HealthChecks__8__Uri + - name: HealthChecksUI__HealthChecks__8__Uri key: internalurls__ordering__hc - - name: HealthChecks-UI__HealthChecks__9__Name + - name: HealthChecksUI__HealthChecks__9__Name key: name__orderingbackground__hc - - name: HealthChecks-UI__HealthChecks__9__Uri + - name: HealthChecksUI__HealthChecks__9__Uri key: internalurls__orderingbackground__hc - - name: HealthChecks-UI__HealthChecks__10__Name + - name: HealthChecksUI__HealthChecks__10__Name key: name__signalrhub__hc - - name: HealthChecks-UI__HealthChecks__10__Uri + - name: HealthChecksUI__HealthChecks__10__Uri key: internalurls__signalrhub__hc - - name: HealthChecks-UI__HealthChecks__11__Name + - name: HealthChecksUI__HealthChecks__11__Name key: name__basket__hc - - name: HealthChecks-UI__HealthChecks__11__Uri + - name: HealthChecksUI__HealthChecks__11__Uri key: internalurls__basket__hc - - name: HealthChecks-UI__HealthChecks__12__Name + - name: HealthChecksUI__HealthChecks__12__Name key: name__catalog__hc - - name: HealthChecks-UI__HealthChecks__12__Uri + - name: HealthChecksUI__HealthChecks__12__Uri key: internalurls__catalog__hc - - name: HealthChecks-UI__HealthChecks__13__Name + - name: HealthChecksUI__HealthChecks__13__Name key: name__identity__hc - - name: HealthChecks-UI__HealthChecks__13__Uri + - name: HealthChecksUI__HealthChecks__13__Uri key: internalurls__identity__hc - - name: HealthChecks-UI__HealthChecks__14__Name + - name: HealthChecksUI__HealthChecks__14__Name key: name__marketing__hc - - name: HealthChecks-UI__HealthChecks__14__Uri + - name: HealthChecksUI__HealthChecks__14__Uri key: internalurls__marketing__hc - - name: HealthChecks-UI__HealthChecks__15__Name + - name: HealthChecksUI__HealthChecks__15__Name key: name__locations__hc - - name: HealthChecks-UI__HealthChecks__15__Uri + - name: HealthChecksUI__HealthChecks__15__Uri key: internalurls__locations__hc - - name: HealthChecks-UI__HealthChecks__16__Name + - name: HealthChecksUI__HealthChecks__16__Name key: name__payment__hc - - name: HealthChecks-UI__HealthChecks__16__Uri + - name: HealthChecksUI__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/nodeports/rabbitmq-admin.yaml b/k8s/nodeports/rabbitmq-admin.yaml new file mode 100644 index 000000000..30d2facf1 --- /dev/null +++ b/k8s/nodeports/rabbitmq-admin.yaml @@ -0,0 +1,12 @@ +kind: Service +apiVersion: v1 +metadata: + name: rabbitmq-admin +spec: + type: NodePort + selector: + app: rabbitmq + ports: + - port: 15672 + nodePort: 31672 + name: rabbitmq-port diff --git a/k8s/nodeports/sql-service.yaml b/k8s/nodeports/sql-service.yaml new file mode 100644 index 000000000..7b0233b68 --- /dev/null +++ b/k8s/nodeports/sql-service.yaml @@ -0,0 +1,12 @@ +kind: Service +apiVersion: v1 +metadata: + name: sql-service +spec: + type: NodePort + selector: + app: sql-data + ports: + - port: 1433 + nodePort: 31433 + name: sql-port diff --git a/run-docker-compose-build.ps1 b/run-docker-compose-build.ps1 index ed4a14541..7d99ee0e7 100644 --- a/run-docker-compose-build.ps1 +++ b/run-docker-compose-build.ps1 @@ -1,6 +1,6 @@ $startTime = $(Get-Date) -docker-compose build --build-arg RUN=scripts/restore-packages +docker-compose build $elapsedTime = $(Get-Date) - $startTime diff --git a/src/ApiGateways/ApiGw-Base/Dockerfile b/src/ApiGateways/ApiGw-Base/Dockerfile index 9dc86a846..d1b50b231 100644 --- a/src/ApiGateways/ApiGw-Base/Dockerfile +++ b/src/ApiGateways/ApiGw-Base/Dockerfile @@ -5,17 +5,51 @@ EXPOSE 80 FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src -COPY scripts scripts/ +# Keep the project list and command dotnet restore identical in all Dockfiles to maximize image cache utilization +COPY eShopOnContainers-ServicesAndWebApps.sln . +COPY docker-compose.dcproj /src/ +COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ +COPY src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj src/ApiGateways/Mobile.Bff.Shopping/aggregator/ +COPY src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj src/ApiGateways/Web.Bff.Shopping/aggregator/ +COPY src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj src/BuildingBlocks/Devspaces.Support/ +COPY src/BuildingBlocks/EventBus/EventBus/EventBus.csproj src/BuildingBlocks/EventBus/EventBus/ +COPY src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj src/BuildingBlocks/EventBus/EventBus.Tests/ +COPY src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj src/BuildingBlocks/EventBus/EventBusRabbitMQ/ +COPY src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj src/BuildingBlocks/EventBus/EventBusServiceBus/ +COPY src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj src/BuildingBlocks/EventBus/IntegrationEventLogEF/ +COPY src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj src/BuildingBlocks/WebHostCustomization/WebHost.Customization/ +COPY src/Services/Basket/Basket.API/Basket.API.csproj src/Services/Basket/Basket.API/ +COPY src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj src/Services/Basket/Basket.FunctionalTests/ +COPY src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj src/Services/Basket/Basket.UnitTests/ +COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj src/Services/Catalog/Catalog.API/ +COPY src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj src/Services/Catalog/Catalog.FunctionalTests/ +COPY src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj src/Services/Catalog/Catalog.UnitTests/ +COPY src/Services/Identity/Identity.API/Identity.API.csproj src/Services/Identity/Identity.API/ +COPY src/Services/Location/Locations.API/Locations.API.csproj src/Services/Location/Locations.API/ +COPY src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj src/Services/Location/Locations.FunctionalTests/ +COPY src/Services/Marketing/Marketing.API/Marketing.API.csproj src/Services/Marketing/Marketing.API/ +COPY src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj src/Services/Marketing/Marketing.FunctionalTests/ +COPY src/Services/Ordering/Ordering.API/Ordering.API.csproj src/Services/Ordering/Ordering.API/ +COPY src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj src/Services/Ordering/Ordering.BackgroundTasks/ +COPY src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj src/Services/Ordering/Ordering.Domain/ +COPY src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj src/Services/Ordering/Ordering.FunctionalTests/ +COPY src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj src/Services/Ordering/Ordering.Infrastructure/ +COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ +COPY src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj src/Services/Ordering/Ordering.UnitTests/ +COPY src/Services/Payment/Payment.API/Payment.API.csproj src/Services/Payment/Payment.API/ +COPY src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj src/Services/Webhooks/Webhooks.API/ +COPY src/Web/WebhookClient/WebhookClient.csproj src/Web/WebhookClient/ +COPY src/Web/WebMVC/WebMVC.csproj src/Web/WebMVC/ +COPY src/Web/WebSPA/WebSPA.csproj src/Web/WebSPA/ +COPY src/Web/WebStatus/WebStatus.csproj src/Web/WebStatus/ +COPY test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj test/ServicesTests/Application.FunctionalTests/ +COPY test/ServicesTests/LoadTest/LoadTest.csproj test/ServicesTests/LoadTest/ -COPY src/ApiGateways/*/*.csproj /src/csproj-files/ -COPY src/ApiGateways/*/*/*.csproj /src/csproj-files/ -COPY src/BuildingBlocks/*/*/*.csproj /src/csproj-files/ -COPY src/Services/*/*/*.csproj /src/csproj-files/ -COPY src/Web/*/*.csproj /src/csproj-files/ +RUN dotnet restore eShopOnContainers-ServicesAndWebApps.sln COPY . . WORKDIR /src/src/ApiGateways/ApiGw-Base/ -RUN dotnet publish -c Release -o /app +RUN dotnet publish --no-restore -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 b1b6b1db6..d5622ccc4 100644 --- a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj +++ b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile index 9b03eccbd..7eb78458e 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile @@ -5,17 +5,51 @@ EXPOSE 80 FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src -COPY scripts scripts/ +# Keep the project list and command dotnet restore identical in all Dockfiles to maximize image cache utilization +COPY eShopOnContainers-ServicesAndWebApps.sln . +COPY docker-compose.dcproj /src/ +COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ +COPY src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj src/ApiGateways/Mobile.Bff.Shopping/aggregator/ +COPY src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj src/ApiGateways/Web.Bff.Shopping/aggregator/ +COPY src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj src/BuildingBlocks/Devspaces.Support/ +COPY src/BuildingBlocks/EventBus/EventBus/EventBus.csproj src/BuildingBlocks/EventBus/EventBus/ +COPY src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj src/BuildingBlocks/EventBus/EventBus.Tests/ +COPY src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj src/BuildingBlocks/EventBus/EventBusRabbitMQ/ +COPY src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj src/BuildingBlocks/EventBus/EventBusServiceBus/ +COPY src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj src/BuildingBlocks/EventBus/IntegrationEventLogEF/ +COPY src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj src/BuildingBlocks/WebHostCustomization/WebHost.Customization/ +COPY src/Services/Basket/Basket.API/Basket.API.csproj src/Services/Basket/Basket.API/ +COPY src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj src/Services/Basket/Basket.FunctionalTests/ +COPY src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj src/Services/Basket/Basket.UnitTests/ +COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj src/Services/Catalog/Catalog.API/ +COPY src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj src/Services/Catalog/Catalog.FunctionalTests/ +COPY src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj src/Services/Catalog/Catalog.UnitTests/ +COPY src/Services/Identity/Identity.API/Identity.API.csproj src/Services/Identity/Identity.API/ +COPY src/Services/Location/Locations.API/Locations.API.csproj src/Services/Location/Locations.API/ +COPY src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj src/Services/Location/Locations.FunctionalTests/ +COPY src/Services/Marketing/Marketing.API/Marketing.API.csproj src/Services/Marketing/Marketing.API/ +COPY src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj src/Services/Marketing/Marketing.FunctionalTests/ +COPY src/Services/Ordering/Ordering.API/Ordering.API.csproj src/Services/Ordering/Ordering.API/ +COPY src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj src/Services/Ordering/Ordering.BackgroundTasks/ +COPY src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj src/Services/Ordering/Ordering.Domain/ +COPY src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj src/Services/Ordering/Ordering.FunctionalTests/ +COPY src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj src/Services/Ordering/Ordering.Infrastructure/ +COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ +COPY src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj src/Services/Ordering/Ordering.UnitTests/ +COPY src/Services/Payment/Payment.API/Payment.API.csproj src/Services/Payment/Payment.API/ +COPY src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj src/Services/Webhooks/Webhooks.API/ +COPY src/Web/WebhookClient/WebhookClient.csproj src/Web/WebhookClient/ +COPY src/Web/WebMVC/WebMVC.csproj src/Web/WebMVC/ +COPY src/Web/WebSPA/WebSPA.csproj src/Web/WebSPA/ +COPY src/Web/WebStatus/WebStatus.csproj src/Web/WebStatus/ +COPY test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj test/ServicesTests/Application.FunctionalTests/ +COPY test/ServicesTests/LoadTest/LoadTest.csproj test/ServicesTests/LoadTest/ -COPY src/ApiGateways/*/*.csproj /src/csproj-files/ -COPY src/ApiGateways/*/*/*.csproj /src/csproj-files/ -COPY src/BuildingBlocks/*/*/*.csproj /src/csproj-files/ -COPY src/Services/*/*/*.csproj /src/csproj-files/ -COPY src/Web/*/*.csproj /src/csproj-files/ +RUN dotnet restore eShopOnContainers-ServicesAndWebApps.sln COPY . . WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator -RUN dotnet publish -c Release -o /app +RUN dotnet publish --no-restore -c Release -o /app FROM build AS publish 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 1dd1ba1b6..5fbc5ec63 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile index fbce2f0ab..e97db900b 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile @@ -5,17 +5,51 @@ EXPOSE 80 FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /src -COPY scripts scripts/ +# Keep the project list and command dotnet restore identical in all Dockfiles to maximize image cache utilization +COPY eShopOnContainers-ServicesAndWebApps.sln . +COPY docker-compose.dcproj /src/ +COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ +COPY src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj src/ApiGateways/Mobile.Bff.Shopping/aggregator/ +COPY src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj src/ApiGateways/Web.Bff.Shopping/aggregator/ +COPY src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj src/BuildingBlocks/Devspaces.Support/ +COPY src/BuildingBlocks/EventBus/EventBus/EventBus.csproj src/BuildingBlocks/EventBus/EventBus/ +COPY src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj src/BuildingBlocks/EventBus/EventBus.Tests/ +COPY src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj src/BuildingBlocks/EventBus/EventBusRabbitMQ/ +COPY src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj src/BuildingBlocks/EventBus/EventBusServiceBus/ +COPY src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj src/BuildingBlocks/EventBus/IntegrationEventLogEF/ +COPY src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj src/BuildingBlocks/WebHostCustomization/WebHost.Customization/ +COPY src/Services/Basket/Basket.API/Basket.API.csproj src/Services/Basket/Basket.API/ +COPY src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj src/Services/Basket/Basket.FunctionalTests/ +COPY src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj src/Services/Basket/Basket.UnitTests/ +COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj src/Services/Catalog/Catalog.API/ +COPY src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj src/Services/Catalog/Catalog.FunctionalTests/ +COPY src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj src/Services/Catalog/Catalog.UnitTests/ +COPY src/Services/Identity/Identity.API/Identity.API.csproj src/Services/Identity/Identity.API/ +COPY src/Services/Location/Locations.API/Locations.API.csproj src/Services/Location/Locations.API/ +COPY src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj src/Services/Location/Locations.FunctionalTests/ +COPY src/Services/Marketing/Marketing.API/Marketing.API.csproj src/Services/Marketing/Marketing.API/ +COPY src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj src/Services/Marketing/Marketing.FunctionalTests/ +COPY src/Services/Ordering/Ordering.API/Ordering.API.csproj src/Services/Ordering/Ordering.API/ +COPY src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj src/Services/Ordering/Ordering.BackgroundTasks/ +COPY src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj src/Services/Ordering/Ordering.Domain/ +COPY src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj src/Services/Ordering/Ordering.FunctionalTests/ +COPY src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj src/Services/Ordering/Ordering.Infrastructure/ +COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ +COPY src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj src/Services/Ordering/Ordering.UnitTests/ +COPY src/Services/Payment/Payment.API/Payment.API.csproj src/Services/Payment/Payment.API/ +COPY src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj src/Services/Webhooks/Webhooks.API/ +COPY src/Web/WebhookClient/WebhookClient.csproj src/Web/WebhookClient/ +COPY src/Web/WebMVC/WebMVC.csproj src/Web/WebMVC/ +COPY src/Web/WebSPA/WebSPA.csproj src/Web/WebSPA/ +COPY src/Web/WebStatus/WebStatus.csproj src/Web/WebStatus/ +COPY test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj test/ServicesTests/Application.FunctionalTests/ +COPY test/ServicesTests/LoadTest/LoadTest.csproj test/ServicesTests/LoadTest/ -COPY src/ApiGateways/*/*.csproj /src/csproj-files/ -COPY src/ApiGateways/*/*/*.csproj /src/csproj-files/ -COPY src/BuildingBlocks/*/*/*.csproj /src/csproj-files/ -COPY src/Services/*/*/*.csproj /src/csproj-files/ -COPY src/Web/*/*.csproj /src/csproj-files/ +RUN dotnet restore eShopOnContainers-ServicesAndWebApps.sln COPY . . WORKDIR /src/src/ApiGateways/Web.Bff.Shopping/aggregator -RUN dotnet publish -c Release -o /app +RUN dotnet publish --no-restore -c Release -o /app FROM build AS publish 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 d438e4602..abfd4ab03 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs index cd2dc557f..53ae33ae0 100644 --- a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs @@ -20,7 +20,7 @@ private readonly SubscriptionClient _subscriptionClient; private readonly ILifetimeScope _autofac; private readonly string AUTOFAC_SCOPE_NAME = "eshop_event_bus"; - private const string INTEGRATION_EVENT_SUFIX = "IntegrationEvent"; + private const string INTEGRATION_EVENT_SUFFIX = "IntegrationEvent"; public EventBusServiceBus(IServiceBusPersisterConnection serviceBusPersisterConnection, ILogger logger, IEventBusSubscriptionsManager subsManager, string subscriptionClientName, @@ -40,7 +40,7 @@ public void Publish(IntegrationEvent @event) { - var eventName = @event.GetType().Name.Replace(INTEGRATION_EVENT_SUFIX, ""); + var eventName = @event.GetType().Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); var jsonMessage = JsonConvert.SerializeObject(@event); var body = Encoding.UTF8.GetBytes(jsonMessage); @@ -61,7 +61,7 @@ public void SubscribeDynamic(string eventName) where TH : IDynamicIntegrationEventHandler { - _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, nameof(TH)); + _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).Name); _subsManager.AddDynamicSubscription(eventName); } @@ -70,7 +70,7 @@ where T : IntegrationEvent where TH : IIntegrationEventHandler { - var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFIX, ""); + var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); var containsKey = _subsManager.HasSubscriptionsForEvent(); if (!containsKey) @@ -89,7 +89,7 @@ } } - _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, nameof(TH)); + _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).Name); _subsManager.AddSubscription(); } @@ -98,7 +98,7 @@ where T : IntegrationEvent where TH : IIntegrationEventHandler { - var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFIX, ""); + var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); try { @@ -135,7 +135,7 @@ _subscriptionClient.RegisterMessageHandler( async (message, token) => { - var eventName = $"{message.Label}{INTEGRATION_EVENT_SUFIX}"; + var eventName = $"{message.Label}{INTEGRATION_EVENT_SUFFIX}"; var messageData = Encoding.UTF8.GetString(message.Body); // Complete the message so that it is not received again. @@ -205,4 +205,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Animations/Base/AnimationBase.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Animations/Base/AnimationBase.cs index 08b978508..95e784b2b 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Animations/Base/AnimationBase.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Animations/Base/AnimationBase.cs @@ -9,10 +9,8 @@ namespace eShopOnContainers.Core.Animations.Base { private bool _isRunning = false; - public static readonly BindableProperty TargetProperty = - BindableProperty.Create("Target", typeof(VisualElement), typeof(AnimationBase), null, - propertyChanged: (bindable, oldValue, newValue) => - ((AnimationBase)bindable).Target = (VisualElement)newValue); + public static readonly BindableProperty TargetProperty = BindableProperty.Create("Target", typeof(VisualElement), typeof(AnimationBase), null, + propertyChanged: (bindable, oldValue, newValue) => ((AnimationBase)bindable).Target = (VisualElement)newValue); public VisualElement Target { @@ -20,10 +18,8 @@ namespace eShopOnContainers.Core.Animations.Base set { SetValue(TargetProperty, value); } } - public static readonly BindableProperty DurationProperty = - BindableProperty.Create("Duration", typeof(string), typeof(AnimationBase), "1000", - propertyChanged: (bindable, oldValue, newValue) => - ((AnimationBase)bindable).Duration = (string)newValue); + public static readonly BindableProperty DurationProperty = BindableProperty.Create("Duration", typeof(string), typeof(AnimationBase), "1000", + propertyChanged: (bindable, oldValue, newValue) => ((AnimationBase)bindable).Duration = (string)newValue); public string Duration { @@ -31,10 +27,8 @@ namespace eShopOnContainers.Core.Animations.Base set { SetValue(DurationProperty, value); } } - public static readonly BindableProperty EasingProperty = - BindableProperty.Create("Easing", typeof(EasingType), typeof(AnimationBase), EasingType.Linear, - propertyChanged: (bindable, oldValue, newValue) => - ((AnimationBase)bindable).Easing = (EasingType)newValue); + public static readonly BindableProperty EasingProperty = BindableProperty.Create("Easing", typeof(EasingType), typeof(AnimationBase), EasingType.Linear, + propertyChanged: (bindable, oldValue, newValue) => ((AnimationBase)bindable).Easing = (EasingType)newValue); public EasingType Easing { @@ -42,10 +36,8 @@ namespace eShopOnContainers.Core.Animations.Base set { SetValue(EasingProperty, value); } } - public static readonly BindableProperty RepeatForeverProperty = - BindableProperty.Create("RepeatForever", typeof(bool), typeof(AnimationBase), false, - propertyChanged: (bindable, oldValue, newValue) => - ((AnimationBase)bindable).RepeatForever = (bool)newValue); + public static readonly BindableProperty RepeatForeverProperty = BindableProperty.Create("RepeatForever", typeof(bool), typeof(AnimationBase), false, + propertyChanged: (bindable, oldValue, newValue) => ((AnimationBase)bindable).RepeatForever = (bool)newValue); public bool RepeatForever { @@ -53,10 +45,8 @@ namespace eShopOnContainers.Core.Animations.Base set { SetValue(RepeatForeverProperty, value); } } - public static readonly BindableProperty DelayProperty = - BindableProperty.Create("Delay", typeof(int), typeof(AnimationBase), 0, - propertyChanged: (bindable, oldValue, newValue) => - ((AnimationBase)bindable).Delay = (int)newValue); + public static readonly BindableProperty DelayProperty = BindableProperty.Create("Delay", typeof(int), typeof(AnimationBase), 0, + propertyChanged: (bindable, oldValue, newValue) => ((AnimationBase)bindable).Delay = (int)newValue); public int Delay { @@ -70,7 +60,6 @@ namespace eShopOnContainers.Core.Animations.Base { try { - if (!_isRunning) { _isRunning = true; @@ -93,6 +82,7 @@ namespace eShopOnContainers.Core.Animations.Base public async Task Reset() { + _isRunning = false; await ResetAnimation(); } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml index 2bccd06d6..b218c1b87 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml @@ -198,7 +198,7 @@